widget.spec.ts 5.0 KB

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