widget.spec.ts 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. // Copyright (c) Jupyter Development Team.
  2. // Distributed under the terms of the Modified BSD License.
  3. import { UUID } from '@lumino/coreutils';
  4. import { Contents, ServiceManager } from '@jupyterlab/services';
  5. import { Message, MessageLoop } from '@lumino/messaging';
  6. import { Widget } from '@lumino/widgets';
  7. import {
  8. Base64ModelFactory,
  9. Context,
  10. DocumentRegistry,
  11. DocumentWidget
  12. } from '@jupyterlab/docregistry';
  13. import { ImageViewer, ImageViewerFactory } from '@jupyterlab/imageviewer';
  14. import { createFileContext } from '@jupyterlab/testutils';
  15. import * as Mock from '@jupyterlab/testutils/lib/mock';
  16. class LogImage extends ImageViewer {
  17. methods: string[] = [];
  18. protected onUpdateRequest(msg: Message): void {
  19. super.onUpdateRequest(msg);
  20. this.methods.push('onUpdateRequest');
  21. }
  22. protected onActivateRequest(msg: Message): void {
  23. super.onActivateRequest(msg);
  24. this.methods.push('onActivateRequest');
  25. }
  26. }
  27. // jsdom does not have createObjectURL and revokeObjectURL, so we define them.
  28. if (typeof window.URL.createObjectURL === 'undefined') {
  29. // eslint-disable-next-line @typescript-eslint/no-empty-function
  30. const noOp = () => {};
  31. Object.defineProperty(window.URL, 'createObjectURL', { value: noOp });
  32. Object.defineProperty(window.URL, 'revokeObjectURL', { value: noOp });
  33. }
  34. /**
  35. * The common image model.
  36. */
  37. const IMAGE: Partial<Contents.IModel> = {
  38. path: UUID.uuid4() + '.png',
  39. type: 'file',
  40. mimetype: 'image/png',
  41. content: 'R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7',
  42. format: 'base64'
  43. };
  44. /**
  45. * The alternate content.
  46. */
  47. const OTHER =
  48. 'iVBORw0KGgoAAAANSUhEUgAAAAUA' +
  49. 'AAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO' +
  50. '9TXL0Y4OHwAAAABJRU5ErkJggg==';
  51. describe('ImageViewer', () => {
  52. const factory = new Base64ModelFactory();
  53. let context: Context<DocumentRegistry.IModel>;
  54. let manager: ServiceManager.IManager;
  55. let widget: LogImage;
  56. beforeAll(async () => {
  57. manager = new Mock.ServiceManagerMock();
  58. await manager.ready;
  59. return manager.contents.save(IMAGE.path!, IMAGE);
  60. });
  61. beforeEach(() => {
  62. context = new Context({ manager, factory, path: IMAGE.path! });
  63. widget = new LogImage(context);
  64. return context.initialize(false);
  65. });
  66. afterEach(() => {
  67. widget.dispose();
  68. });
  69. describe('#constructor()', () => {
  70. it('should create an ImageViewer', () => {
  71. expect(widget).toBeInstanceOf(ImageViewer);
  72. });
  73. it('should keep the title in sync with the file name', async () => {
  74. const newPath = ((IMAGE as any).path = UUID.uuid4() + '.png');
  75. expect(widget.title.label).toBe(context.path);
  76. let called = false;
  77. context.pathChanged.connect(() => {
  78. expect(widget.title.label).toBe(newPath);
  79. called = true;
  80. });
  81. await manager.contents.rename(context.path, newPath);
  82. expect(called).toBe(true);
  83. });
  84. it('should set the content after the context is ready', async () => {
  85. await context.ready;
  86. MessageLoop.sendMessage(widget, Widget.Msg.UpdateRequest);
  87. const img = widget.node.querySelector('img') as HTMLImageElement;
  88. expect(img.src).toContain(IMAGE.content);
  89. });
  90. it('should handle a change to the content', async () => {
  91. await context.ready;
  92. context.model.fromString(OTHER);
  93. MessageLoop.sendMessage(widget, Widget.Msg.UpdateRequest);
  94. const img = widget.node.querySelector('img') as HTMLImageElement;
  95. expect(img.src).toContain(OTHER);
  96. });
  97. });
  98. describe('#context', () => {
  99. it('should be the context associated with the widget', () => {
  100. expect(widget.context).toBe(context);
  101. });
  102. });
  103. describe('#scale', () => {
  104. it('should default to 1', () => {
  105. expect(widget.scale).toBe(1);
  106. });
  107. it('should be settable', () => {
  108. widget.scale = 0.5;
  109. expect(widget.scale).toBe(0.5);
  110. });
  111. });
  112. describe('#dispose()', () => {
  113. it('should dispose of the resources used by the widget', () => {
  114. expect(widget.isDisposed).toBe(false);
  115. widget.dispose();
  116. expect(widget.isDisposed).toBe(true);
  117. widget.dispose();
  118. expect(widget.isDisposed).toBe(true);
  119. });
  120. });
  121. describe('#onUpdateRequest()', () => {
  122. it('should render the image', async () => {
  123. const img: HTMLImageElement = widget.node.querySelector('img')!;
  124. await widget.ready;
  125. MessageLoop.sendMessage(widget, Widget.Msg.UpdateRequest);
  126. expect(widget.methods).toContain('onUpdateRequest');
  127. expect(img.src).toContain(IMAGE.content);
  128. });
  129. });
  130. describe('#onActivateRequest()', () => {
  131. it('should focus the widget', () => {
  132. Widget.attach(widget, document.body);
  133. MessageLoop.sendMessage(widget, Widget.Msg.ActivateRequest);
  134. expect(widget.methods).toContain('onActivateRequest');
  135. expect(widget.node.contains(document.activeElement)).toBe(true);
  136. });
  137. });
  138. });
  139. describe('ImageViewerFactory', () => {
  140. describe('#createNewWidget', () => {
  141. it('should create an image document widget', async () => {
  142. const factory = new ImageViewerFactory({
  143. name: 'Image',
  144. modelName: 'base64',
  145. fileTypes: ['png'],
  146. defaultFor: ['png']
  147. });
  148. const context = await createFileContext(
  149. IMAGE.path,
  150. new Mock.ServiceManagerMock()
  151. );
  152. const d = factory.createNew(context);
  153. expect(d).toBeInstanceOf(DocumentWidget);
  154. expect(d.content).toBeInstanceOf(ImageViewer);
  155. });
  156. });
  157. });