executionindicator.spec.tsx 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. // Copyright (c) Jupyter Development Team.
  2. // Distributed under the terms of the Modified BSD License.
  3. import { ISessionContext, SessionContext } from '@jupyterlab/apputils';
  4. import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
  5. import { createSessionContext } from '@jupyterlab/testutils';
  6. import { JupyterServer } from '@jupyterlab/testutils/lib/start_jupyter_server';
  7. import {
  8. ExecutionIndicator,
  9. ExecutionIndicatorComponent,
  10. Notebook,
  11. NotebookActions,
  12. NotebookModel
  13. } from '..';
  14. import * as utils from './utils';
  15. import React from 'react';
  16. import ReactDOMServer from 'react-dom/server';
  17. const fastCellModel = {
  18. cell_type: 'code',
  19. execution_count: 1,
  20. metadata: { tags: [] },
  21. outputs: [],
  22. source: ['print("hello")\n']
  23. };
  24. const slowCellModel = {
  25. cell_type: 'code',
  26. execution_count: 1,
  27. metadata: { tags: [] },
  28. outputs: [],
  29. source: ['import time\n', 'time.sleep(3)\n']
  30. };
  31. const server = new JupyterServer();
  32. beforeAll(async () => {
  33. jest.setTimeout(20000);
  34. await server.start();
  35. });
  36. afterAll(async () => {
  37. await server.shutdown();
  38. });
  39. describe('@jupyterlab/notebook', () => {
  40. let rendermime: IRenderMimeRegistry;
  41. describe('ExecutionIndicator', () => {
  42. let widget: Notebook;
  43. let sessionContext: ISessionContext;
  44. let ipySessionContext: ISessionContext;
  45. let indicator: ExecutionIndicator;
  46. beforeAll(async function () {
  47. jest.setTimeout(20000);
  48. rendermime = utils.defaultRenderMime();
  49. async function createContext(options?: Partial<SessionContext.IOptions>) {
  50. const context = await createSessionContext(options);
  51. await context.initialize();
  52. await context.session?.kernel?.info;
  53. return context;
  54. }
  55. [sessionContext, ipySessionContext] = await Promise.all([
  56. createContext(),
  57. createContext({ kernelPreference: { name: 'ipython' } })
  58. ]);
  59. });
  60. beforeEach(async () => {
  61. widget = new Notebook({
  62. rendermime,
  63. contentFactory: utils.createNotebookFactory(),
  64. mimeTypeService: utils.mimeTypeService
  65. });
  66. const model = new NotebookModel();
  67. const modelJson = {
  68. ...utils.DEFAULT_CONTENT,
  69. cells: [fastCellModel, slowCellModel, slowCellModel, fastCellModel]
  70. };
  71. model.fromJSON(modelJson);
  72. widget.model = model;
  73. model.sharedModel.clearUndoHistory();
  74. widget.activeCellIndex = 0;
  75. for (let idx = 0; idx < widget.widgets.length; idx++) {
  76. widget.select(widget.widgets[idx]);
  77. }
  78. indicator = new ExecutionIndicator();
  79. indicator.model.attachNotebook({
  80. content: widget,
  81. context: ipySessionContext
  82. });
  83. await ipySessionContext.restartKernel();
  84. });
  85. afterEach(() => {
  86. widget.dispose();
  87. utils.clipboard.clear();
  88. indicator.dispose();
  89. });
  90. afterAll(async () => {
  91. await Promise.all([
  92. sessionContext.shutdown(),
  93. ipySessionContext.shutdown()
  94. ]);
  95. });
  96. describe('executedAllCell', () => {
  97. it('should count correctly number of scheduled cell', async () => {
  98. let scheduledCell: number | undefined = 0;
  99. indicator.model.stateChanged.connect(state => {
  100. scheduledCell = state.executionState(widget)!.scheduledCellNumber;
  101. });
  102. await NotebookActions.run(widget, ipySessionContext);
  103. expect(scheduledCell).toBe(4);
  104. });
  105. it('should count correctly elapsed time', async () => {
  106. let elapsedTime: number | undefined = 0;
  107. indicator.model.stateChanged.connect(state => {
  108. elapsedTime = state.executionState(widget)!.totalTime;
  109. });
  110. await NotebookActions.run(widget, ipySessionContext);
  111. expect(elapsedTime).toBeGreaterThanOrEqual(6);
  112. });
  113. it('should tick every second', async () => {
  114. let tick: Array<number> = [];
  115. indicator.model.stateChanged.connect(state => {
  116. tick.push(state.executionState(widget)!.totalTime);
  117. });
  118. await NotebookActions.run(widget, ipySessionContext);
  119. expect(tick).toEqual(expect.arrayContaining([1, 2, 3, 4, 5, 6, 6]));
  120. });
  121. it('should count correctly number of executed requests', async () => {
  122. let executed: Array<number> = [];
  123. indicator.model.stateChanged.connect(state => {
  124. executed.push(state.executionState(widget)!.scheduledCell.size);
  125. });
  126. await NotebookActions.run(widget, ipySessionContext);
  127. expect(executed).toEqual(expect.arrayContaining([3, 3, 3, 2, 2, 2, 0]));
  128. });
  129. });
  130. });
  131. describe('testProgressCircle', () => {
  132. let displayOption: { showOnToolBar: boolean; showProgress: boolean };
  133. let defaultState: {
  134. interval: number;
  135. kernelStatus: ISessionContext.KernelDisplayStatus;
  136. executionStatus: string;
  137. needReset: boolean;
  138. scheduledCell: Set<string>;
  139. scheduledCellNumber: number;
  140. timeout: number;
  141. totalTime: number;
  142. };
  143. const EMPTY_CIRCLE = 'M 0 0 v -104 A 104 104 1 0 0 -0.0000 -104.0000 z';
  144. const HALF_FILLED_CIRCLE = 'M 0 0 v -104 A 104 104 1 0 0 0.0000 104.0000 z';
  145. const FILLED_CIRCLE = 'M 0 0 v -104 A 104 104 1 1 0 0.1815 -103.9998 z';
  146. beforeEach(() => {
  147. displayOption = { showOnToolBar: false, showProgress: true };
  148. defaultState = {
  149. interval: 0,
  150. kernelStatus: 'idle',
  151. executionStatus: 'idle',
  152. needReset: false,
  153. scheduledCell: new Set<string>(),
  154. scheduledCellNumber: 0,
  155. timeout: 0,
  156. totalTime: 0
  157. };
  158. });
  159. it('Should render an empty div with undefined state', () => {
  160. const element = (
  161. <ExecutionIndicatorComponent
  162. displayOption={displayOption}
  163. state={undefined}
  164. translator={undefined}
  165. />
  166. );
  167. const htmlElement = ReactDOMServer.renderToString(element);
  168. expect(htmlElement).toContain('<div data-reactroot=""></div>');
  169. });
  170. it('Should render a filled circle with 0/2 cell executed message', () => {
  171. defaultState.scheduledCellNumber = 2;
  172. defaultState.scheduledCell.add('foo');
  173. defaultState.scheduledCell.add('bar');
  174. defaultState.executionStatus = 'busy';
  175. defaultState.totalTime = 1;
  176. const element = (
  177. <ExecutionIndicatorComponent
  178. displayOption={displayOption}
  179. state={defaultState}
  180. translator={undefined}
  181. />
  182. );
  183. const htmlElement = ReactDOMServer.renderToString(element);
  184. expect(htmlElement).toContain(FILLED_CIRCLE);
  185. expect(htmlElement).toContain(`Executed 0/2 requests`);
  186. });
  187. it('Should render a half filled circle with 1/2 cell executed message', () => {
  188. defaultState.scheduledCellNumber = 2;
  189. defaultState.scheduledCell.add('foo');
  190. defaultState.executionStatus = 'busy';
  191. defaultState.totalTime = 1;
  192. const element = (
  193. <ExecutionIndicatorComponent
  194. displayOption={displayOption}
  195. state={defaultState}
  196. translator={undefined}
  197. />
  198. );
  199. const htmlElement = ReactDOMServer.renderToString(element);
  200. expect(htmlElement).toContain(HALF_FILLED_CIRCLE);
  201. expect(htmlElement).toContain(`Executed 1/2 requests`);
  202. });
  203. it('Should render an empty circle with 2 requests executed message', () => {
  204. defaultState.scheduledCellNumber = 2;
  205. defaultState.executionStatus = 'idle';
  206. defaultState.totalTime = 1;
  207. const element = (
  208. <ExecutionIndicatorComponent
  209. displayOption={displayOption}
  210. state={defaultState}
  211. translator={undefined}
  212. />
  213. );
  214. const htmlElement = ReactDOMServer.renderToString(element);
  215. expect(htmlElement).toContain(EMPTY_CIRCLE);
  216. expect(htmlElement).toContain(`Executed 2 requests`);
  217. });
  218. });
  219. });