default-toolbar.spec.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  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. ]);
  200. });
  201. });
  202. });
  203. describe('kernelRequired', () => {
  204. let context: Context<INotebookModel>;
  205. let panel: NotebookPanel;
  206. beforeEach(async function () {
  207. context = await utils.createMockContext(true);
  208. panel = utils.createNotebookPanel(context);
  209. context.model.fromJSON(utils.DEFAULT_CONTENT);
  210. });
  211. afterEach(async () => {
  212. await context.sessionContext.shutdown();
  213. panel.dispose();
  214. context.dispose();
  215. });
  216. describe('#createRunButton()', () => {
  217. it('should run and advance when clicked', async () => {
  218. const button = ToolbarItems.createRunButton(panel);
  219. const widget = panel.content;
  220. // Clear and select the first two cells.
  221. const codeCell = widget.widgets[0] as CodeCell;
  222. codeCell.model.outputs.clear();
  223. widget.select(codeCell);
  224. const mdCell = widget.widgets[1] as MarkdownCell;
  225. mdCell.rendered = false;
  226. widget.select(mdCell);
  227. Widget.attach(button, document.body);
  228. await context.sessionContext.session!.kernel!.info;
  229. const delegate = new PromiseDelegate();
  230. panel.sessionContext.iopubMessage.connect((_, msg) => {
  231. if (KernelMessage.isExecuteInputMsg(msg)) {
  232. delegate.resolve(void 0);
  233. }
  234. });
  235. simulate(button.node.firstChild as HTMLElement, 'mousedown');
  236. await delegate.promise;
  237. button.dispose();
  238. });
  239. it("should add an inline svg node with the 'run' icon", async () => {
  240. const button = ToolbarItems.createRunButton(panel);
  241. Widget.attach(button, document.body);
  242. await framePromise();
  243. expect(button.node.querySelector("[data-icon$='run']")).toBeDefined();
  244. });
  245. });
  246. describe('#createRestartRunAllButton()', () => {
  247. it('should restart and run all when clicked', async () => {
  248. const button = ToolbarItems.createRestartRunAllButton(panel);
  249. const widget = panel.content;
  250. // Clear the first two cells.
  251. const codeCell = widget.widgets[0] as CodeCell;
  252. codeCell.model.outputs.clear();
  253. const mdCell = widget.widgets[1] as MarkdownCell;
  254. mdCell.rendered = false;
  255. Widget.attach(button, document.body);
  256. await panel.sessionContext.ready;
  257. const delegate = new PromiseDelegate();
  258. panel.sessionContext.iopubMessage.connect((_, msg) => {
  259. if (KernelMessage.isExecuteInputMsg(msg)) {
  260. delegate.resolve(void 0);
  261. }
  262. });
  263. simulate(button.node.firstChild as HTMLElement, 'mousedown');
  264. await acceptDialog();
  265. await delegate.promise;
  266. button.dispose();
  267. });
  268. it("should add an inline svg node with the 'fast-forward' icon", async () => {
  269. const button = ToolbarItems.createRestartRunAllButton(panel);
  270. Widget.attach(button, document.body);
  271. await framePromise();
  272. expect(
  273. button.node.querySelector("[data-icon$='fast-forward']")
  274. ).toBeDefined();
  275. });
  276. });
  277. });
  278. });
  279. });