reopen-project-menu-manager-spec.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. /** @babel */
  2. import { Disposable } from 'event-kit';
  3. const ReopenProjectMenuManager = require('../src/reopen-project-menu-manager');
  4. function numberRange(low, high) {
  5. const size = high - low;
  6. const result = new Array(size);
  7. for (var i = 0; i < size; i++) result[i] = low + i;
  8. return result;
  9. }
  10. describe('ReopenProjectMenuManager', () => {
  11. let menuManager, commandRegistry, config, historyManager, reopenProjects;
  12. let commandDisposable, configDisposable, historyDisposable;
  13. let openFunction;
  14. beforeEach(() => {
  15. menuManager = jasmine.createSpyObj('MenuManager', ['add']);
  16. menuManager.add.andReturn(new Disposable());
  17. commandRegistry = jasmine.createSpyObj('CommandRegistry', ['add']);
  18. commandDisposable = jasmine.createSpyObj('Disposable', ['dispose']);
  19. commandRegistry.add.andReturn(commandDisposable);
  20. config = jasmine.createSpyObj('Config', ['onDidChange', 'get']);
  21. config.get.andReturn(10);
  22. configDisposable = jasmine.createSpyObj('Disposable', ['dispose']);
  23. config.didChangeListener = {};
  24. config.onDidChange.andCallFake((key, fn) => {
  25. config.didChangeListener[key] = fn;
  26. return configDisposable;
  27. });
  28. historyManager = jasmine.createSpyObj('historyManager', [
  29. 'getProjects',
  30. 'onDidChangeProjects'
  31. ]);
  32. historyManager.getProjects.andReturn([]);
  33. historyDisposable = jasmine.createSpyObj('Disposable', ['dispose']);
  34. historyManager.onDidChangeProjects.andCallFake(fn => {
  35. historyManager.changeProjectsListener = fn;
  36. return historyDisposable;
  37. });
  38. openFunction = jasmine.createSpy();
  39. reopenProjects = new ReopenProjectMenuManager({
  40. menu: menuManager,
  41. commands: commandRegistry,
  42. history: historyManager,
  43. config,
  44. open: openFunction
  45. });
  46. });
  47. describe('constructor', () => {
  48. it("registers the 'reopen-project' command function", () => {
  49. expect(commandRegistry.add).toHaveBeenCalled();
  50. const cmdCall = commandRegistry.add.calls[0];
  51. expect(cmdCall.args.length).toBe(2);
  52. expect(cmdCall.args[0]).toBe('atom-workspace');
  53. expect(typeof cmdCall.args[1]['application:reopen-project']).toBe(
  54. 'function'
  55. );
  56. });
  57. });
  58. describe('dispose', () => {
  59. it('disposes of the history, command and config disposables', () => {
  60. reopenProjects.dispose();
  61. expect(historyDisposable.dispose).toHaveBeenCalled();
  62. expect(configDisposable.dispose).toHaveBeenCalled();
  63. expect(commandDisposable.dispose).toHaveBeenCalled();
  64. });
  65. it('disposes of the menu disposable once used', () => {
  66. const menuDisposable = jasmine.createSpyObj('Disposable', ['dispose']);
  67. menuManager.add.andReturn(menuDisposable);
  68. reopenProjects.update();
  69. expect(menuDisposable.dispose).not.toHaveBeenCalled();
  70. reopenProjects.dispose();
  71. expect(menuDisposable.dispose).toHaveBeenCalled();
  72. });
  73. });
  74. describe('the command', () => {
  75. it('calls open with the paths of the project specified by the detail index', () => {
  76. historyManager.getProjects.andReturn([
  77. { paths: ['/a'] },
  78. { paths: ['/b', 'c:\\'] }
  79. ]);
  80. reopenProjects.update();
  81. const reopenProjectCommand =
  82. commandRegistry.add.calls[0].args[1]['application:reopen-project'];
  83. reopenProjectCommand({ detail: { index: 1 } });
  84. expect(openFunction).toHaveBeenCalled();
  85. expect(openFunction.calls[0].args[0]).toEqual(['/b', 'c:\\']);
  86. });
  87. it('does not call open when no command detail is supplied', () => {
  88. const reopenProjectCommand =
  89. commandRegistry.add.calls[0].args[1]['application:reopen-project'];
  90. reopenProjectCommand({});
  91. expect(openFunction).not.toHaveBeenCalled();
  92. });
  93. it('does not call open when no command detail index is supplied', () => {
  94. const reopenProjectCommand =
  95. commandRegistry.add.calls[0].args[1]['application:reopen-project'];
  96. reopenProjectCommand({ detail: { anything: 'here' } });
  97. expect(openFunction).not.toHaveBeenCalled();
  98. });
  99. });
  100. describe('update', () => {
  101. it('adds menu items to MenuManager based on projects from HistoryManager', () => {
  102. historyManager.getProjects.andReturn([
  103. { paths: ['/a'] },
  104. { paths: ['/b', 'c:\\'] }
  105. ]);
  106. reopenProjects.update();
  107. expect(historyManager.getProjects).toHaveBeenCalled();
  108. expect(menuManager.add).toHaveBeenCalled();
  109. const menuArg = menuManager.add.calls[0].args[0];
  110. expect(menuArg.length).toBe(1);
  111. expect(menuArg[0].label).toBe('File');
  112. expect(menuArg[0].submenu.length).toBe(1);
  113. const projectsMenu = menuArg[0].submenu[0];
  114. expect(projectsMenu.label).toBe('Reopen Project');
  115. expect(projectsMenu.submenu.length).toBe(2);
  116. const first = projectsMenu.submenu[0];
  117. expect(first.label).toBe('/a');
  118. expect(first.command).toBe('application:reopen-project');
  119. expect(first.commandDetail).toEqual({ index: 0, paths: ['/a'] });
  120. const second = projectsMenu.submenu[1];
  121. expect(second.label).toBe('b, c:\\');
  122. expect(second.command).toBe('application:reopen-project');
  123. expect(second.commandDetail).toEqual({ index: 1, paths: ['/b', 'c:\\'] });
  124. });
  125. it("adds only the number of menu items specified in the 'core.reopenProjectMenuCount' config", () => {
  126. historyManager.getProjects.andReturn(
  127. numberRange(1, 100).map(i => ({ paths: ['/test/' + i] }))
  128. );
  129. reopenProjects.update();
  130. expect(menuManager.add).toHaveBeenCalled();
  131. const menu = menuManager.add.calls[0].args[0][0];
  132. expect(menu.label).toBe('File');
  133. expect(menu.submenu.length).toBe(1);
  134. expect(menu.submenu[0].label).toBe('Reopen Project');
  135. expect(menu.submenu[0].submenu.length).toBe(10);
  136. });
  137. it('disposes the previously menu built', () => {
  138. const menuDisposable = jasmine.createSpyObj('Disposable', ['dispose']);
  139. menuManager.add.andReturn(menuDisposable);
  140. reopenProjects.update();
  141. expect(menuDisposable.dispose).not.toHaveBeenCalled();
  142. reopenProjects.update();
  143. expect(menuDisposable.dispose).toHaveBeenCalled();
  144. });
  145. it("is called when the Config changes for 'core.reopenProjectMenuCount'", () => {
  146. historyManager.getProjects.andReturn(
  147. numberRange(1, 100).map(i => ({ paths: ['/test/' + i] }))
  148. );
  149. reopenProjects.update();
  150. config.get.andReturn(25);
  151. config.didChangeListener['core.reopenProjectMenuCount']({
  152. oldValue: 10,
  153. newValue: 25
  154. });
  155. const finalArgs = menuManager.add.calls[1].args[0];
  156. const projectsMenu = finalArgs[0].submenu[0].submenu;
  157. expect(projectsMenu.length).toBe(25);
  158. });
  159. it("is called when the HistoryManager's projects change", () => {
  160. reopenProjects.update();
  161. historyManager.getProjects.andReturn([
  162. { paths: ['/a'] },
  163. { paths: ['/b', 'c:\\'] }
  164. ]);
  165. historyManager.changeProjectsListener();
  166. expect(menuManager.add.calls.length).toBe(2);
  167. const finalArgs = menuManager.add.calls[1].args[0];
  168. const projectsMenu = finalArgs[0].submenu[0];
  169. const first = projectsMenu.submenu[0];
  170. expect(first.label).toBe('/a');
  171. expect(first.command).toBe('application:reopen-project');
  172. expect(first.commandDetail).toEqual({ index: 0, paths: ['/a'] });
  173. const second = projectsMenu.submenu[1];
  174. expect(second.label).toBe('b, c:\\');
  175. expect(second.command).toBe('application:reopen-project');
  176. expect(second.commandDetail).toEqual({ index: 1, paths: ['/b', 'c:\\'] });
  177. });
  178. });
  179. describe('updateProjects', () => {
  180. it('creates correct menu items commands for recent projects', () => {
  181. const projects = [
  182. { paths: ['/users/neila'] },
  183. { paths: ['/users/buzza', 'users/michaelc'] }
  184. ];
  185. const menu = ReopenProjectMenuManager.createProjectsMenu(projects);
  186. expect(menu.label).toBe('File');
  187. expect(menu.submenu.length).toBe(1);
  188. const recentMenu = menu.submenu[0];
  189. expect(recentMenu.label).toBe('Reopen Project');
  190. expect(recentMenu.submenu.length).toBe(2);
  191. const first = recentMenu.submenu[0];
  192. expect(first.label).toBe('/users/neila');
  193. expect(first.command).toBe('application:reopen-project');
  194. expect(first.commandDetail).toEqual({
  195. index: 0,
  196. paths: ['/users/neila']
  197. });
  198. const second = recentMenu.submenu[1];
  199. expect(second.label).toBe('buzza, michaelc');
  200. expect(second.command).toBe('application:reopen-project');
  201. expect(second.commandDetail).toEqual({
  202. index: 1,
  203. paths: ['/users/buzza', 'users/michaelc']
  204. });
  205. });
  206. });
  207. describe('createLabel', () => {
  208. it('returns the Unix path unchanged if there is only one', () => {
  209. const label = ReopenProjectMenuManager.createLabel({
  210. paths: ['/a/b/c/d/e/f']
  211. });
  212. expect(label).toBe('/a/b/c/d/e/f');
  213. });
  214. it('returns the Windows path unchanged if there is only one', () => {
  215. const label = ReopenProjectMenuManager.createLabel({
  216. paths: ['c:\\missions\\apollo11']
  217. });
  218. expect(label).toBe('c:\\missions\\apollo11');
  219. });
  220. it('returns the URL unchanged if there is only one', () => {
  221. const label = ReopenProjectMenuManager.createLabel({
  222. paths: ['https://launch.pad/apollo/11']
  223. });
  224. expect(label).toBe('https://launch.pad/apollo/11');
  225. });
  226. it('returns a comma-separated list of base names if there are multiple', () => {
  227. const project = {
  228. paths: ['/var/one', '/usr/bin/two', '/etc/mission/control/three']
  229. };
  230. const label = ReopenProjectMenuManager.createLabel(project);
  231. expect(label).toBe('one, two, three');
  232. });
  233. describe('betterBaseName', () => {
  234. it('returns the standard base name for an absolute Unix path', () => {
  235. const name = ReopenProjectMenuManager.betterBaseName('/one/to/three');
  236. expect(name).toBe('three');
  237. });
  238. it('returns the standard base name for a relative Windows path', () => {
  239. if (process.platform === 'win32') {
  240. const name = ReopenProjectMenuManager.betterBaseName('.\\one\\two');
  241. expect(name).toBe('two');
  242. }
  243. });
  244. it('returns the standard base name for an absolute Windows path', () => {
  245. if (process.platform === 'win32') {
  246. const name = ReopenProjectMenuManager.betterBaseName(
  247. 'c:\\missions\\apollo\\11'
  248. );
  249. expect(name).toBe('11');
  250. }
  251. });
  252. it('returns the drive root for a Windows drive name', () => {
  253. const name = ReopenProjectMenuManager.betterBaseName('d:');
  254. expect(name).toBe('d:\\');
  255. });
  256. it('returns the drive root for a Windows drive root', () => {
  257. const name = ReopenProjectMenuManager.betterBaseName('e:\\');
  258. expect(name).toBe('e:\\');
  259. });
  260. it('returns the final path for a URI', () => {
  261. const name = ReopenProjectMenuManager.betterBaseName(
  262. 'https://something/else'
  263. );
  264. expect(name).toBe('else');
  265. });
  266. });
  267. });
  268. });