openfiledialog.spec.ts 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. // Copyright (c) Jupyter Development Team.
  2. // Distributed under the terms of the Modified BSD License.
  3. import 'jest';
  4. import expect from 'expect';
  5. // Copyright (c) Jupyter Development Team.
  6. // Distributed under the terms of the Modified BSD License.
  7. import { toArray } from '@lumino/algorithm';
  8. import { DocumentManager, IDocumentManager } from '@jupyterlab/docmanager';
  9. import { DocumentRegistry, TextModelFactory } from '@jupyterlab/docregistry';
  10. import { FileDialog, FilterFileBrowserModel, FileBrowserModel } from '../src';
  11. import { ServiceManager, Contents } from '@jupyterlab/services';
  12. import {
  13. acceptDialog,
  14. dismissDialog,
  15. waitForDialog,
  16. sleep,
  17. framePromise
  18. } from '@jupyterlab/testutils';
  19. import * as Mock from '@jupyterlab/testutils/lib/mock';
  20. import { simulate } from 'simulate-event';
  21. describe('@jupyterlab/filebrowser', () => {
  22. let manager: IDocumentManager;
  23. let serviceManager: ServiceManager.IManager;
  24. let registry: DocumentRegistry;
  25. beforeAll(async () => {
  26. const opener: DocumentManager.IWidgetOpener = {
  27. open: widget => {
  28. /* no op */
  29. }
  30. };
  31. registry = new DocumentRegistry({
  32. textModelFactory: new TextModelFactory()
  33. });
  34. serviceManager = new Mock.ServiceManagerMock();
  35. manager = new DocumentManager({
  36. registry,
  37. opener,
  38. manager: serviceManager
  39. });
  40. const contents = serviceManager.contents;
  41. await contents.newUntitled({ type: 'directory' });
  42. await contents.newUntitled({ type: 'file' });
  43. await contents.newUntitled({ type: 'notebook' });
  44. });
  45. describe('FilterFileBrowserModel', () => {
  46. describe('#constructor()', () => {
  47. it('should construct a new filtered file browser model', () => {
  48. const model = new FilterFileBrowserModel({ manager });
  49. expect(model).toBeInstanceOf(FilterFileBrowserModel);
  50. });
  51. it('should accept filter option', () => {
  52. const model = new FilterFileBrowserModel({
  53. manager,
  54. filter: (model: Contents.IModel) => false
  55. });
  56. expect(model).toBeInstanceOf(FilterFileBrowserModel);
  57. });
  58. });
  59. describe('#items()', () => {
  60. it('should list all elements if no filter is defined', async () => {
  61. const filteredModel = new FilterFileBrowserModel({
  62. manager
  63. });
  64. await filteredModel.cd();
  65. const model = new FileBrowserModel({ manager });
  66. await model.cd();
  67. const filteredItems = toArray(filteredModel.items());
  68. const items = toArray(model.items());
  69. expect(filteredItems.length).toBe(items.length);
  70. });
  71. it('should list all directories whatever the filter', async () => {
  72. const filteredModel = new FilterFileBrowserModel({
  73. manager,
  74. filter: (model: Contents.IModel) => false
  75. });
  76. await filteredModel.cd();
  77. const model = new FileBrowserModel({ manager });
  78. await model.cd();
  79. const filteredItems = toArray(filteredModel.items());
  80. const items = toArray(model.items());
  81. const folders = items.filter(item => item.type === 'directory');
  82. expect(filteredItems.length).toBe(folders.length);
  83. });
  84. it('should respect the filter', async () => {
  85. const filteredModel = new FilterFileBrowserModel({
  86. manager,
  87. filter: (model: Contents.IModel) => model.type === 'notebook'
  88. });
  89. await filteredModel.cd();
  90. const model = new FileBrowserModel({ manager });
  91. await model.cd();
  92. const filteredItems = toArray(
  93. filteredModel.items()
  94. ) as Contents.IModel[];
  95. const items = toArray(model.items());
  96. const shownItems = items.filter(
  97. item => item.type === 'directory' || item.type === 'notebook'
  98. );
  99. expect(filteredItems.length).toBe(shownItems.length);
  100. const notebooks = filteredItems.filter(
  101. item => item.type === 'notebook'
  102. );
  103. expect(notebooks.length).toBeGreaterThan(0);
  104. });
  105. });
  106. });
  107. describe('FileDialog.getOpenFiles()', () => {
  108. it('should create a dialog', async () => {
  109. const dialog = FileDialog.getOpenFiles({
  110. manager
  111. });
  112. await dismissDialog();
  113. const result = await dialog;
  114. expect(result.button.accept).toBe(false);
  115. expect(result.value).toBeNull();
  116. });
  117. it('should accept options', async () => {
  118. const node = document.createElement('div');
  119. document.body.appendChild(node);
  120. const dialog = FileDialog.getOpenFiles({
  121. manager,
  122. title: 'Select a notebook',
  123. host: node,
  124. filter: (value: Contents.IModel) => value.type === 'notebook'
  125. });
  126. await acceptDialog();
  127. const result = await dialog;
  128. expect(result.button.accept).toBe(true);
  129. const items = result.value!;
  130. expect(items.length).toBe(1);
  131. document.body.removeChild(node);
  132. });
  133. it('should return one selected file', async () => {
  134. const node = document.createElement('div');
  135. document.body.appendChild(node);
  136. const dialog = FileDialog.getOpenFiles({
  137. manager,
  138. title: 'Select a notebook',
  139. host: node,
  140. filter: (value: Contents.IModel) => value.type === 'notebook'
  141. });
  142. await waitForDialog();
  143. await framePromise();
  144. let counter = 0;
  145. const listing = node.getElementsByClassName('jp-DirListing-content')[0];
  146. expect(listing).toBeTruthy();
  147. let items = listing.getElementsByTagName('li');
  148. counter = 0;
  149. // Wait for the directory listing to be populated
  150. while (items.length === 0 && counter < 100) {
  151. await sleep(10);
  152. items = listing.getElementsByTagName('li');
  153. counter++;
  154. }
  155. // Fails if there is no items shown
  156. expect(items.length).toBeGreaterThan(0);
  157. // Emulate notebook file selection
  158. const item = listing.querySelector('li[data-file-type="notebook"]')!;
  159. simulate(item, 'mousedown');
  160. await acceptDialog();
  161. const result = await dialog;
  162. const files = result.value!;
  163. expect(files.length).toBe(1);
  164. expect(files[0].type).toBe('notebook');
  165. expect(files[0].name).toEqual(expect.stringMatching(/Untitled.*.ipynb/));
  166. document.body.removeChild(node);
  167. });
  168. it('should return current path if nothing is selected', async () => {
  169. const dialog = FileDialog.getOpenFiles({
  170. manager
  171. });
  172. await acceptDialog();
  173. const result = await dialog;
  174. const items = result.value!;
  175. expect(items.length).toBe(1);
  176. expect(items[0].type).toBe('directory');
  177. expect(items[0].path).toBe('');
  178. });
  179. });
  180. describe('FileDialog.getExistingDirectory()', () => {
  181. it('should create a dialog', async () => {
  182. const dialog = FileDialog.getExistingDirectory({
  183. manager
  184. });
  185. await dismissDialog();
  186. const result = await dialog;
  187. expect(result.button.accept).toBe(false);
  188. expect(result.value).toBeNull();
  189. });
  190. it('should accept options', async () => {
  191. const node = document.createElement('div');
  192. document.body.appendChild(node);
  193. const dialog = FileDialog.getExistingDirectory({
  194. manager,
  195. title: 'Select a folder',
  196. host: node
  197. });
  198. await acceptDialog();
  199. const result = await dialog;
  200. expect(result.button.accept).toBe(true);
  201. expect(result.value!.length).toBe(1);
  202. document.body.removeChild(node);
  203. });
  204. it('should return one selected directory', async () => {
  205. const node = document.createElement('div');
  206. document.body.appendChild(node);
  207. const dialog = FileDialog.getExistingDirectory({
  208. manager,
  209. title: 'Select a folder',
  210. host: node
  211. });
  212. await waitForDialog();
  213. await framePromise();
  214. let counter = 0;
  215. const listing = node.getElementsByClassName('jp-DirListing-content')[0];
  216. expect(listing).toBeTruthy();
  217. let items = listing.getElementsByTagName('li');
  218. // Wait for the directory listing to be populated
  219. while (items.length === 0 && counter < 100) {
  220. await sleep(10);
  221. items = listing.getElementsByTagName('li');
  222. counter++;
  223. }
  224. // Fails if there is no items shown
  225. expect(items.length).toBeGreaterThan(0);
  226. // Emulate notebook file selection
  227. simulate(items.item(items.length - 1)!, 'mousedown');
  228. await acceptDialog();
  229. const result = await dialog;
  230. const files = result.value!;
  231. expect(files.length).toBe(1);
  232. expect(files[0].type).toBe('directory');
  233. expect(files[0].name).toEqual(
  234. expect.stringMatching(/Untitled Folder( \d+)?/)
  235. );
  236. document.body.removeChild(node);
  237. });
  238. it('should return current path if nothing is selected', async () => {
  239. const dialog = FileDialog.getExistingDirectory({
  240. manager
  241. });
  242. await acceptDialog();
  243. const result = await dialog;
  244. const items = result.value!;
  245. expect(items.length).toBe(1);
  246. expect(items[0].type).toBe('directory');
  247. expect(items[0].path).toBe('');
  248. });
  249. });
  250. });