default-toolbar.spec.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. // Copyright (c) Jupyter Development Team.
  2. // Distributed under the terms of the Modified BSD License.
  3. import { CodeCell, MarkdownCell } from '@jupyterlab/cells';
  4. import { Context } from '@jupyterlab/docregistry';
  5. import { KernelMessage } from '@jupyterlab/services';
  6. import {
  7. acceptDialog,
  8. framePromise,
  9. signalToPromise,
  10. sleep
  11. } from '@jupyterlab/testutils';
  12. import { PromiseDelegate } from '@lumino/coreutils';
  13. import { Widget } from '@lumino/widgets';
  14. import { simulate } from 'simulate-event';
  15. import {
  16. INotebookModel,
  17. NotebookActions,
  18. NotebookPanel,
  19. ToolbarItems
  20. } from '..';
  21. import * as utils from './utils';
  22. const JUPYTER_CELL_MIME = 'application/vnd.jupyter.cells';
  23. describe('@jupyterlab/notebook', () => {
  24. describe('ToolbarItems', () => {
  25. describe('noKernel', () => {
  26. let context: Context<INotebookModel>;
  27. let panel: NotebookPanel;
  28. beforeEach(async () => {
  29. context = await utils.createMockContext();
  30. panel = utils.createNotebookPanel(context);
  31. context.model.fromJSON(utils.DEFAULT_CONTENT);
  32. });
  33. afterEach(() => {
  34. panel.dispose();
  35. context.dispose();
  36. });
  37. describe('#createSaveButton()', () => {
  38. it('should save when clicked', async () => {
  39. const button = ToolbarItems.createSaveButton(panel);
  40. Widget.attach(button, document.body);
  41. const promise = signalToPromise(context.fileChanged);
  42. await framePromise();
  43. simulate(button.node.firstChild as HTMLElement, 'mousedown');
  44. await promise;
  45. button.dispose();
  46. });
  47. it("should add an inline svg node with the 'save' icon", async () => {
  48. const button = ToolbarItems.createSaveButton(panel);
  49. Widget.attach(button, document.body);
  50. await framePromise();
  51. expect(
  52. button.node.querySelector("[data-icon$='save']")
  53. ).toBeDefined();
  54. });
  55. });
  56. describe('#createInsertButton()', () => {
  57. it('should insert below when clicked', async () => {
  58. const button = ToolbarItems.createInsertButton(panel);
  59. Widget.attach(button, document.body);
  60. await framePromise();
  61. simulate(button.node.firstChild as HTMLElement, 'mousedown');
  62. expect(panel.content.activeCellIndex).toBe(1);
  63. expect(panel.content.activeCell).toBeInstanceOf(CodeCell);
  64. button.dispose();
  65. });
  66. it("should add an inline svg node with the 'add' icon", async () => {
  67. const button = ToolbarItems.createInsertButton(panel);
  68. Widget.attach(button, document.body);
  69. await framePromise();
  70. expect(button.node.querySelector("[data-icon$='add']")).toBeDefined();
  71. button.dispose();
  72. });
  73. });
  74. describe('#createCutButton()', () => {
  75. it('should cut when clicked', async () => {
  76. const button = ToolbarItems.createCutButton(panel);
  77. const count = panel.content.widgets.length;
  78. Widget.attach(button, document.body);
  79. await framePromise();
  80. simulate(button.node.firstChild as HTMLElement, 'mousedown');
  81. expect(panel.content.widgets.length).toBe(count - 1);
  82. expect(utils.clipboard.hasData(JUPYTER_CELL_MIME)).toBe(true);
  83. button.dispose();
  84. });
  85. it("should add an inline svg node with the 'cut' icon", async () => {
  86. const button = ToolbarItems.createCutButton(panel);
  87. Widget.attach(button, document.body);
  88. await framePromise();
  89. expect(button.node.querySelector("[data-icon$='cut']")).toBeDefined();
  90. button.dispose();
  91. });
  92. });
  93. describe('#createCopyButton()', () => {
  94. it('should copy when clicked', async () => {
  95. const button = ToolbarItems.createCopyButton(panel);
  96. const count = panel.content.widgets.length;
  97. Widget.attach(button, document.body);
  98. await framePromise();
  99. simulate(button.node.firstChild as HTMLElement, 'mousedown');
  100. expect(panel.content.widgets.length).toBe(count);
  101. expect(utils.clipboard.hasData(JUPYTER_CELL_MIME)).toBe(true);
  102. button.dispose();
  103. });
  104. it("should add an inline svg node with the 'copy' icon", async () => {
  105. const button = ToolbarItems.createCopyButton(panel);
  106. Widget.attach(button, document.body);
  107. await framePromise();
  108. expect(
  109. button.node.querySelector("[data-icon$='copy']")
  110. ).toBeDefined();
  111. button.dispose();
  112. });
  113. });
  114. describe('#createPasteButton()', () => {
  115. it('should paste when clicked', async () => {
  116. const button = ToolbarItems.createPasteButton(panel);
  117. const count = panel.content.widgets.length;
  118. Widget.attach(button, document.body);
  119. await framePromise();
  120. NotebookActions.copy(panel.content);
  121. simulate(button.node.firstChild as HTMLElement, 'mousedown');
  122. await sleep();
  123. expect(panel.content.widgets.length).toBe(count + 1);
  124. button.dispose();
  125. });
  126. it("should add an inline svg node with the 'paste' icon", async () => {
  127. const button = ToolbarItems.createPasteButton(panel);
  128. Widget.attach(button, document.body);
  129. await framePromise();
  130. expect(
  131. button.node.querySelector("[data-icon$='paste']")
  132. ).toBeDefined();
  133. button.dispose();
  134. });
  135. });
  136. describe('#createCellTypeItem()', () => {
  137. it('should track the cell type of the current cell', async () => {
  138. const item = ToolbarItems.createCellTypeItem(panel);
  139. Widget.attach(item, document.body);
  140. await framePromise();
  141. const node = item.node.getElementsByTagName(
  142. 'select'
  143. )[0] as HTMLSelectElement;
  144. expect(node.value).toBe('code');
  145. panel.content.activeCellIndex++;
  146. await framePromise();
  147. expect(node.value).toBe('markdown');
  148. item.dispose();
  149. });
  150. it("should display `'-'` if multiple cell types are selected", async () => {
  151. const item = ToolbarItems.createCellTypeItem(panel);
  152. Widget.attach(item, document.body);
  153. await framePromise();
  154. const node = item.node.getElementsByTagName(
  155. 'select'
  156. )[0] as HTMLSelectElement;
  157. expect(node.value).toBe('code');
  158. panel.content.select(panel.content.widgets[1]);
  159. await framePromise();
  160. expect(node.value).toBe('-');
  161. item.dispose();
  162. });
  163. it('should display the active cell type if multiple cells of the same type are selected', async () => {
  164. const item = ToolbarItems.createCellTypeItem(panel);
  165. Widget.attach(item, document.body);
  166. await framePromise();
  167. const node = item.node.getElementsByTagName(
  168. 'select'
  169. )[0] as HTMLSelectElement;
  170. expect(node.value).toBe('code');
  171. const cell = panel.model!.contentFactory.createCodeCell({});
  172. panel.model!.cells.insert(1, cell);
  173. panel.content.select(panel.content.widgets[1]);
  174. await framePromise();
  175. expect(node.value).toBe('code');
  176. item.dispose();
  177. });
  178. });
  179. describe('#getDefaultItems()', () => {
  180. it('should return the default items of the panel toolbar', () => {
  181. const names = ToolbarItems.getDefaultItems(panel).map(item => {
  182. const name = item.name;
  183. item.widget.dispose();
  184. return name;
  185. });
  186. expect(names).toEqual([
  187. 'save',
  188. 'insert',
  189. 'cut',
  190. 'copy',
  191. 'paste',
  192. 'run',
  193. 'interrupt',
  194. 'restart',
  195. 'restart-and-run',
  196. 'cellType',
  197. 'spacer',
  198. 'kernelName',
  199. 'kernelStatus'
  200. ]);
  201. });
  202. });
  203. });
  204. describe('kernelRequired', () => {
  205. let context: Context<INotebookModel>;
  206. let panel: NotebookPanel;
  207. beforeEach(async function () {
  208. context = await utils.createMockContext(true);
  209. panel = utils.createNotebookPanel(context);
  210. context.model.fromJSON(utils.DEFAULT_CONTENT);
  211. });
  212. afterEach(async () => {
  213. await context.sessionContext.shutdown();
  214. panel.dispose();
  215. context.dispose();
  216. });
  217. describe('#createRunButton()', () => {
  218. it('should run and advance when clicked', async () => {
  219. const button = ToolbarItems.createRunButton(panel);
  220. const widget = panel.content;
  221. // Clear and select the first two cells.
  222. const codeCell = widget.widgets[0] as CodeCell;
  223. codeCell.model.outputs.clear();
  224. widget.select(codeCell);
  225. const mdCell = widget.widgets[1] as MarkdownCell;
  226. mdCell.rendered = false;
  227. widget.select(mdCell);
  228. Widget.attach(button, document.body);
  229. await context.sessionContext.session!.kernel!.info;
  230. const delegate = new PromiseDelegate();
  231. panel.sessionContext.iopubMessage.connect((_, msg) => {
  232. if (KernelMessage.isExecuteInputMsg(msg)) {
  233. delegate.resolve(void 0);
  234. }
  235. });
  236. simulate(button.node.firstChild as HTMLElement, 'mousedown');
  237. await delegate.promise;
  238. button.dispose();
  239. });
  240. it("should add an inline svg node with the 'run' icon", async () => {
  241. const button = ToolbarItems.createRunButton(panel);
  242. Widget.attach(button, document.body);
  243. await framePromise();
  244. expect(button.node.querySelector("[data-icon$='run']")).toBeDefined();
  245. });
  246. });
  247. describe('#createRestartRunAllButton()', () => {
  248. it('should restart and run all when clicked', async () => {
  249. const button = ToolbarItems.createRestartRunAllButton(panel);
  250. const widget = panel.content;
  251. // Clear the first two cells.
  252. const codeCell = widget.widgets[0] as CodeCell;
  253. codeCell.model.outputs.clear();
  254. const mdCell = widget.widgets[1] as MarkdownCell;
  255. mdCell.rendered = false;
  256. Widget.attach(button, document.body);
  257. await panel.sessionContext.ready;
  258. const delegate = new PromiseDelegate();
  259. panel.sessionContext.iopubMessage.connect((_, msg) => {
  260. if (KernelMessage.isExecuteInputMsg(msg)) {
  261. delegate.resolve(void 0);
  262. }
  263. });
  264. simulate(button.node.firstChild as HTMLElement, 'mousedown');
  265. await acceptDialog();
  266. await delegate.promise;
  267. button.dispose();
  268. });
  269. it("should add an inline svg node with the 'fast-forward' icon", async () => {
  270. const button = ToolbarItems.createRestartRunAllButton(panel);
  271. Widget.attach(button, document.body);
  272. await framePromise();
  273. expect(
  274. button.node.querySelector("[data-icon$='fast-forward']")
  275. ).toBeDefined();
  276. });
  277. });
  278. });
  279. });
  280. });