layoutrestorer.spec.ts 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. // Copyright (c) Jupyter Development Team.
  2. // Distributed under the terms of the Modified BSD License.
  3. import { ILabShell, LayoutRestorer } from '@jupyterlab/application';
  4. import { WidgetTracker } from '@jupyterlab/apputils';
  5. import { StateDB } from '@jupyterlab/statedb';
  6. import { CommandRegistry } from '@lumino/commands';
  7. import { PromiseDelegate } from '@lumino/coreutils';
  8. import { Widget } from '@lumino/widgets';
  9. describe('apputils', () => {
  10. describe('LayoutRestorer', () => {
  11. describe('#constructor()', () => {
  12. it('should construct a new layout restorer', () => {
  13. const restorer = new LayoutRestorer({
  14. connector: new StateDB(),
  15. first: Promise.resolve<void>(void 0),
  16. registry: new CommandRegistry()
  17. });
  18. expect(restorer).toBeInstanceOf(LayoutRestorer);
  19. });
  20. });
  21. describe('#restored', () => {
  22. it('should be a promise available right away', () => {
  23. const restorer = new LayoutRestorer({
  24. connector: new StateDB(),
  25. first: Promise.resolve<void>(void 0),
  26. registry: new CommandRegistry()
  27. });
  28. expect(restorer.restored).toBeInstanceOf(Promise);
  29. });
  30. it('should resolve when restorer is done', async () => {
  31. const ready = new PromiseDelegate<void>();
  32. const restorer = new LayoutRestorer({
  33. connector: new StateDB(),
  34. first: ready.promise,
  35. registry: new CommandRegistry()
  36. });
  37. const promise = restorer.restored;
  38. ready.resolve(void 0);
  39. await promise;
  40. });
  41. });
  42. describe('#add()', () => {
  43. it('should add a widget to be tracked by the restorer', async () => {
  44. const ready = new PromiseDelegate<void>();
  45. const restorer = new LayoutRestorer({
  46. connector: new StateDB(),
  47. first: ready.promise,
  48. registry: new CommandRegistry()
  49. });
  50. const currentWidget = new Widget();
  51. const dehydrated: ILabShell.ILayout = {
  52. mainArea: { currentWidget, dock: null },
  53. leftArea: { collapsed: true, currentWidget: null, widgets: null },
  54. rightArea: { collapsed: true, currentWidget: null, widgets: null }
  55. };
  56. restorer.add(currentWidget, 'test-one');
  57. ready.resolve(void 0);
  58. await restorer.restored;
  59. await restorer.save(dehydrated);
  60. const layout = await restorer.fetch();
  61. expect(layout.mainArea?.currentWidget).toBe(currentWidget);
  62. });
  63. });
  64. describe('#fetch()', () => {
  65. it('should always return a value', async () => {
  66. const restorer = new LayoutRestorer({
  67. connector: new StateDB(),
  68. first: Promise.resolve(void 0),
  69. registry: new CommandRegistry()
  70. });
  71. const layout = await restorer.fetch();
  72. expect(layout).not.toBe(null);
  73. });
  74. it('should fetch saved data', async () => {
  75. const ready = new PromiseDelegate<void>();
  76. const restorer = new LayoutRestorer({
  77. connector: new StateDB(),
  78. first: ready.promise,
  79. registry: new CommandRegistry()
  80. });
  81. const currentWidget = new Widget();
  82. // The `fresh` attribute is only here to check against the return value.
  83. const dehydrated: ILabShell.ILayout = {
  84. fresh: false,
  85. mainArea: { currentWidget: null, dock: null },
  86. leftArea: {
  87. currentWidget,
  88. collapsed: true,
  89. widgets: [currentWidget]
  90. },
  91. rightArea: { collapsed: true, currentWidget: null, widgets: null }
  92. };
  93. restorer.add(currentWidget, 'test-one');
  94. ready.resolve(void 0);
  95. await restorer.restored;
  96. await restorer.save(dehydrated);
  97. const layout = await restorer.fetch();
  98. expect(layout).toEqual(dehydrated);
  99. });
  100. });
  101. describe('#restore()', () => {
  102. it('should restore the widgets in a tracker', async () => {
  103. const tracker = new WidgetTracker({ namespace: 'foo-widget' });
  104. const registry = new CommandRegistry();
  105. const state = new StateDB();
  106. const ready = new PromiseDelegate<void>();
  107. const restorer = new LayoutRestorer({
  108. connector: state,
  109. first: ready.promise,
  110. registry
  111. });
  112. let called = false;
  113. const key = `${tracker.namespace}:${tracker.namespace}`;
  114. registry.addCommand(tracker.namespace, {
  115. execute: () => {
  116. called = true;
  117. }
  118. });
  119. await state.save(key, { data: null });
  120. ready.resolve(undefined);
  121. await restorer.restore(tracker, {
  122. name: () => tracker.namespace,
  123. command: tracker.namespace
  124. });
  125. await restorer.restored;
  126. expect(called).toBe(true);
  127. });
  128. });
  129. describe('#save()', () => {
  130. it('should not run before `first` promise', async () => {
  131. const restorer = new LayoutRestorer({
  132. connector: new StateDB(),
  133. first: new Promise(() => {
  134. // no op
  135. }),
  136. registry: new CommandRegistry()
  137. });
  138. const dehydrated: ILabShell.ILayout = {
  139. mainArea: { currentWidget: null, dock: null },
  140. leftArea: { currentWidget: null, collapsed: true, widgets: null },
  141. rightArea: { collapsed: true, currentWidget: null, widgets: null }
  142. };
  143. await expect(restorer.save(dehydrated)).rejects.toBe(
  144. 'save() was called prematurely.'
  145. );
  146. });
  147. it('should save data', async () => {
  148. const ready = new PromiseDelegate<void>();
  149. const restorer = new LayoutRestorer({
  150. connector: new StateDB(),
  151. first: ready.promise,
  152. registry: new CommandRegistry()
  153. });
  154. const currentWidget = new Widget();
  155. // The `fresh` attribute is only here to check against the return value.
  156. const dehydrated: ILabShell.ILayout = {
  157. fresh: false,
  158. mainArea: { currentWidget: null, dock: null },
  159. leftArea: {
  160. currentWidget,
  161. collapsed: true,
  162. widgets: [currentWidget]
  163. },
  164. rightArea: { collapsed: true, currentWidget: null, widgets: null }
  165. };
  166. restorer.add(currentWidget, 'test-one');
  167. ready.resolve(void 0);
  168. await restorer.restored;
  169. await restorer.save(dehydrated);
  170. const layout = await restorer.fetch();
  171. expect(layout).toEqual(dehydrated);
  172. });
  173. });
  174. });
  175. });