fileactionsSpec.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739
  1. /**
  2. * SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors
  3. * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
  4. * SPDX-License-Identifier: AGPL-3.0-or-later
  5. */
  6. describe('OCA.Files.FileActions tests', function() {
  7. var fileList, fileActions, clock;
  8. beforeEach(function() {
  9. clock = sinon.useFakeTimers();
  10. // init horrible parameters
  11. var $body = $('#testArea');
  12. $body.append('<input type="hidden" id="permissions" value="31"></input>');
  13. $body.append('<table class="files-filestable list-container view-grid"><tbody class="files-fileList"></tbody></table>');
  14. // dummy files table
  15. fileActions = new OCA.Files.FileActions();
  16. fileActions.registerAction({
  17. name: 'Testdropdown',
  18. displayName: 'Testdropdowndisplay',
  19. mime: 'all',
  20. permissions: OC.PERMISSION_READ,
  21. icon: function () {
  22. return OC.imagePath('core', 'actions/download');
  23. }
  24. });
  25. fileActions.registerAction({
  26. name: 'Testinline',
  27. displayName: 'Testinlinedisplay',
  28. type: OCA.Files.FileActions.TYPE_INLINE,
  29. mime: 'all',
  30. permissions: OC.PERMISSION_READ
  31. });
  32. fileActions.registerAction({
  33. name: 'Testdefault',
  34. displayName: 'Testdefaultdisplay',
  35. mime: 'all',
  36. permissions: OC.PERMISSION_READ
  37. });
  38. fileActions.setDefault('all', 'Testdefault');
  39. fileList = new OCA.Files.FileList($body, {
  40. fileActions: fileActions
  41. });
  42. fileList.changeDirectory('/subdir', false, true);
  43. });
  44. afterEach(function() {
  45. fileActions = null;
  46. fileList.destroy();
  47. fileList = undefined;
  48. clock.restore();
  49. $('#permissions, .files-filestable').remove();
  50. });
  51. it('calling clear() clears file actions', function() {
  52. fileActions.clear();
  53. expect(fileActions.actions).toEqual({});
  54. expect(fileActions.defaults).toEqual({});
  55. expect(fileActions.icons).toEqual({});
  56. expect(fileActions.currentFile).toBe(null);
  57. });
  58. describe('displaying actions', function() {
  59. var $tr;
  60. beforeEach(function() {
  61. var fileData = {
  62. id: 18,
  63. type: 'file',
  64. name: 'testName.txt',
  65. mimetype: 'text/plain',
  66. size: '1234',
  67. etag: 'a01234c',
  68. mtime: '123456',
  69. permissions: OC.PERMISSION_READ | OC.PERMISSION_UPDATE
  70. };
  71. // note: FileActions.display() is called implicitly
  72. $tr = fileList.add(fileData);
  73. });
  74. it('renders inline file actions', function() {
  75. // actions defined after call
  76. expect($tr.find('.action.action-testinline').length).toEqual(1);
  77. expect($tr.find('.action.action-testinline').attr('data-action')).toEqual('Testinline');
  78. });
  79. it('does not render dropdown actions', function() {
  80. expect($tr.find('.action.action-testdropdown').length).toEqual(0);
  81. });
  82. it('does not render default action', function() {
  83. expect($tr.find('.action.action-testdefault').length).toEqual(0);
  84. });
  85. it('replaces file actions when displayed twice', function() {
  86. fileActions.display($tr.find('td.filename'), true, fileList);
  87. fileActions.display($tr.find('td.filename'), true, fileList);
  88. expect($tr.find('.action.action-testinline').length).toEqual(1);
  89. });
  90. it('renders actions menu trigger', function() {
  91. expect($tr.find('.action.action-menu').length).toEqual(1);
  92. expect($tr.find('.action.action-menu').attr('data-action')).toEqual('menu');
  93. });
  94. it('only renders actions relevant to the mime type', function() {
  95. fileActions.registerAction({
  96. name: 'Match',
  97. displayName: 'MatchDisplay',
  98. type: OCA.Files.FileActions.TYPE_INLINE,
  99. mime: 'text/plain',
  100. permissions: OC.PERMISSION_READ
  101. });
  102. fileActions.registerAction({
  103. name: 'Nomatch',
  104. displayName: 'NoMatchDisplay',
  105. type: OCA.Files.FileActions.TYPE_INLINE,
  106. mime: 'application/octet-stream',
  107. permissions: OC.PERMISSION_READ
  108. });
  109. fileActions.display($tr.find('td.filename'), true, fileList);
  110. expect($tr.find('.action.action-match').length).toEqual(1);
  111. expect($tr.find('.action.action-nomatch').length).toEqual(0);
  112. });
  113. it('only renders actions relevant to the permissions', function() {
  114. fileActions.registerAction({
  115. name: 'Match',
  116. displayName: 'MatchDisplay',
  117. type: OCA.Files.FileActions.TYPE_INLINE,
  118. mime: 'text/plain',
  119. permissions: OC.PERMISSION_UPDATE
  120. });
  121. fileActions.registerAction({
  122. name: 'Nomatch',
  123. displayName: 'NoMatchDisplay',
  124. type: OCA.Files.FileActions.TYPE_INLINE,
  125. mime: 'text/plain',
  126. permissions: OC.PERMISSION_DELETE
  127. });
  128. fileActions.display($tr.find('td.filename'), true, fileList);
  129. expect($tr.find('.action.action-match').length).toEqual(1);
  130. expect($tr.find('.action.action-nomatch').length).toEqual(0);
  131. });
  132. it('display inline icon with image path', function() {
  133. fileActions.registerAction({
  134. name: 'Icon',
  135. displayName: 'IconDisplay',
  136. type: OCA.Files.FileActions.TYPE_INLINE,
  137. mime: 'all',
  138. icon: OC.imagePath('core', 'actions/icon'),
  139. permissions: OC.PERMISSION_READ
  140. });
  141. fileActions.registerAction({
  142. name: 'NoIcon',
  143. displayName: 'NoIconDisplay',
  144. type: OCA.Files.FileActions.TYPE_INLINE,
  145. mime: 'all',
  146. permissions: OC.PERMISSION_READ
  147. });
  148. fileActions.display($tr.find('td.filename'), true, fileList);
  149. expect($tr.find('.action.action-icon').length).toEqual(1);
  150. expect($tr.find('.action.action-icon').find('img').length).toEqual(1);
  151. expect($tr.find('.action.action-icon').find('img').eq(0).attr('src')).toEqual(OC.imagePath('core', 'actions/icon'));
  152. expect($tr.find('.action.action-noicon').length).toEqual(1);
  153. expect($tr.find('.action.action-noicon').find('img').length).toEqual(0);
  154. });
  155. it('display alt text on inline icon with image path', function() {
  156. fileActions.registerAction({
  157. name: 'IconAltText',
  158. displayName: 'IconAltTextDisplay',
  159. type: OCA.Files.FileActions.TYPE_INLINE,
  160. mime: 'all',
  161. icon: OC.imagePath('core', 'actions/iconAltText'),
  162. altText: 'alt icon text',
  163. permissions: OC.PERMISSION_READ
  164. });
  165. fileActions.registerAction({
  166. name: 'IconNoAltText',
  167. displayName: 'IconNoAltTextDisplay',
  168. type: OCA.Files.FileActions.TYPE_INLINE,
  169. mime: 'all',
  170. icon: OC.imagePath('core', 'actions/iconNoAltText'),
  171. permissions: OC.PERMISSION_READ
  172. });
  173. fileActions.display($tr.find('td.filename'), true, fileList);
  174. expect($tr.find('.action.action-iconalttext').length).toEqual(1);
  175. expect($tr.find('.action.action-iconalttext').find('img').length).toEqual(1);
  176. expect($tr.find('.action.action-iconalttext').find('img').eq(0).attr('alt')).toEqual('alt icon text');
  177. expect($tr.find('.action.action-iconnoalttext').length).toEqual(1);
  178. expect($tr.find('.action.action-iconnoalttext').find('img').length).toEqual(1);
  179. expect($tr.find('.action.action-iconnoalttext').find('img').eq(0).attr('alt')).toEqual('');
  180. });
  181. it('display inline icon with iconClass', function() {
  182. fileActions.registerAction({
  183. name: 'Icon',
  184. displayName: 'IconDisplay',
  185. type: OCA.Files.FileActions.TYPE_INLINE,
  186. mime: 'all',
  187. iconClass: 'icon-test',
  188. permissions: OC.PERMISSION_READ
  189. });
  190. fileActions.registerAction({
  191. name: 'NoIcon',
  192. displayName: 'NoIconDisplay',
  193. type: OCA.Files.FileActions.TYPE_INLINE,
  194. mime: 'all',
  195. permissions: OC.PERMISSION_READ
  196. });
  197. fileActions.display($tr.find('td.filename'), true, fileList);
  198. expect($tr.find('.action.action-icon').length).toEqual(1);
  199. expect($tr.find('.action.action-icon').find('.icon').length).toEqual(1);
  200. expect($tr.find('.action.action-icon').find('.icon').hasClass('icon-test')).toEqual(true);
  201. expect($tr.find('.action.action-noicon').length).toEqual(1);
  202. expect($tr.find('.action.action-noicon').find('.icon').length).toEqual(0);
  203. });
  204. it('display alt text on inline icon with iconClass when no display name exists', function() {
  205. fileActions.registerAction({
  206. name: 'IconAltText',
  207. displayName: '',
  208. type: OCA.Files.FileActions.TYPE_INLINE,
  209. mime: 'all',
  210. iconClass: 'icon-alttext',
  211. altText: 'alt icon text',
  212. permissions: OC.PERMISSION_READ
  213. });
  214. fileActions.registerAction({
  215. name: 'IconNoAltText',
  216. displayName: 'IconNoAltTextDisplay',
  217. type: OCA.Files.FileActions.TYPE_INLINE,
  218. mime: 'all',
  219. altText: 'useless alt text',
  220. iconClass: 'icon-noalttext',
  221. permissions: OC.PERMISSION_READ
  222. });
  223. fileActions.display($tr.find('td.filename'), true, fileList);
  224. expect($tr.find('.action.action-iconalttext').length).toEqual(1);
  225. expect($tr.find('.action.action-iconalttext').find('.icon').length).toEqual(1);
  226. expect($tr.find('.action.action-iconalttext').find('.hidden-visually').text()).toEqual('alt icon text');
  227. expect($tr.find('.action.action-iconnoalttext').length).toEqual(1);
  228. expect($tr.find('.action.action-iconnoalttext').find('.icon').length).toEqual(1);
  229. expect($tr.find('.action.action-iconnoalttext').find('.hidden-visually').length).toEqual(0);
  230. });
  231. });
  232. describe('action handler', function() {
  233. var actionStub, $tr, clock;
  234. beforeEach(function() {
  235. clock = sinon.useFakeTimers();
  236. var fileData = {
  237. id: 18,
  238. type: 'file',
  239. name: 'testName.txt',
  240. mimetype: 'text/plain',
  241. size: '1234',
  242. etag: 'a01234c',
  243. mtime: '123456'
  244. };
  245. actionStub = sinon.stub();
  246. fileActions.registerAction({
  247. name: 'Test',
  248. type: OCA.Files.FileActions.TYPE_INLINE,
  249. mime: 'all',
  250. icon: OC.imagePath('core', 'actions/test'),
  251. permissions: OC.PERMISSION_READ,
  252. actionHandler: actionStub
  253. });
  254. $tr = fileList.add(fileData);
  255. });
  256. afterEach(function() {
  257. OC.hideMenus();
  258. // jump past animations
  259. clock.tick(1000);
  260. clock.restore();
  261. });
  262. it('passes context to action handler', function() {
  263. var notifyUpdateListenersSpy = sinon.spy(fileList.fileActions, '_notifyUpdateListeners');
  264. expect($tr.length).toEqual(1);
  265. expect($tr.find('.action-test').length).toEqual(1);
  266. $tr.find('.action-test').click();
  267. expect(actionStub.calledOnce).toEqual(true);
  268. expect(actionStub.getCall(0).args[0]).toEqual('testName.txt');
  269. var context = actionStub.getCall(0).args[1];
  270. expect(context.$file.is($tr)).toEqual(true);
  271. expect(context.fileList).toBeDefined();
  272. expect(context.fileActions).toBeDefined();
  273. expect(context.dir).toEqual('/subdir');
  274. expect(context.fileInfoModel.get('name')).toEqual('testName.txt');
  275. expect(notifyUpdateListenersSpy.calledTwice).toEqual(true);
  276. expect(notifyUpdateListenersSpy.calledBefore(actionStub)).toEqual(true);
  277. expect(notifyUpdateListenersSpy.calledAfter(actionStub)).toEqual(true);
  278. expect(notifyUpdateListenersSpy.getCall(0).args[0]).toEqual('beforeTriggerAction');
  279. expect(notifyUpdateListenersSpy.getCall(0).args[1]).toEqual({
  280. action: fileActions.getActions('all', OCA.Files.FileActions.TYPE_INLINE, OC.PERMISSION_READ)['Test'],
  281. fileName: 'testName.txt',
  282. context: context
  283. });
  284. expect(notifyUpdateListenersSpy.getCall(1).args[0]).toEqual('afterTriggerAction');
  285. expect(notifyUpdateListenersSpy.getCall(1).args[1]).toEqual({
  286. action: fileActions.getActions('all', OCA.Files.FileActions.TYPE_INLINE, OC.PERMISSION_READ)['Test'],
  287. fileName: 'testName.txt',
  288. context: context
  289. });
  290. // when data-path is defined
  291. actionStub.reset();
  292. $tr.attr('data-path', '/somepath');
  293. $tr.find('.action-test').click();
  294. context = actionStub.getCall(0).args[1];
  295. expect(context.dir).toEqual('/somepath');
  296. });
  297. it('also triggers action handler when calling triggerAction()', function() {
  298. var notifyUpdateListenersSpy = sinon.spy(fileList.fileActions, '_notifyUpdateListeners');
  299. var model = new OCA.Files.FileInfoModel({
  300. id: 1,
  301. name: 'Test.txt',
  302. path: '/subdir',
  303. mime: 'text/plain',
  304. permissions: 31
  305. });
  306. fileActions.triggerAction('Test', model, fileList);
  307. expect(actionStub.calledOnce).toEqual(true);
  308. expect(actionStub.getCall(0).args[0]).toEqual('Test.txt');
  309. expect(actionStub.getCall(0).args[1].fileList).toEqual(fileList);
  310. expect(actionStub.getCall(0).args[1].fileActions).toEqual(fileActions);
  311. expect(actionStub.getCall(0).args[1].fileInfoModel).toEqual(model);
  312. expect(notifyUpdateListenersSpy.calledTwice).toEqual(true);
  313. expect(notifyUpdateListenersSpy.calledBefore(actionStub)).toEqual(true);
  314. expect(notifyUpdateListenersSpy.calledAfter(actionStub)).toEqual(true);
  315. expect(notifyUpdateListenersSpy.getCall(0).args[0]).toEqual('beforeTriggerAction');
  316. expect(notifyUpdateListenersSpy.getCall(0).args[1]).toEqual({
  317. action: fileActions.getActions('all', OCA.Files.FileActions.TYPE_INLINE, OC.PERMISSION_READ)['Test'],
  318. fileName: 'Test.txt',
  319. context: {
  320. fileActions: fileActions,
  321. fileInfoModel: model,
  322. dir: '/subdir',
  323. fileList: fileList,
  324. $file: fileList.findFileEl('Test.txt')
  325. }
  326. });
  327. expect(notifyUpdateListenersSpy.getCall(1).args[0]).toEqual('afterTriggerAction');
  328. expect(notifyUpdateListenersSpy.getCall(1).args[1]).toEqual({
  329. action: fileActions.getActions('all', OCA.Files.FileActions.TYPE_INLINE, OC.PERMISSION_READ)['Test'],
  330. fileName: 'Test.txt',
  331. context: {
  332. fileActions: fileActions,
  333. fileInfoModel: model,
  334. dir: '/subdir',
  335. fileList: fileList,
  336. $file: fileList.findFileEl('Test.txt')
  337. }
  338. });
  339. });
  340. it('triggers listener events when invoked directly', function() {
  341. var context = {fileActions: new OCA.Files.FileActions()}
  342. var notifyUpdateListenersSpy = sinon.spy(context.fileActions, '_notifyUpdateListeners');
  343. var testAction = fileActions.get('all', OCA.Files.FileActions.TYPE_INLINE, OC.PERMISSION_READ)['Test'];
  344. testAction('Test.txt', context);
  345. expect(actionStub.calledOnce).toEqual(true);
  346. expect(actionStub.getCall(0).args[0]).toEqual('Test.txt');
  347. expect(actionStub.getCall(0).args[1]).toBe(context);
  348. expect(notifyUpdateListenersSpy.calledTwice).toEqual(true);
  349. expect(notifyUpdateListenersSpy.calledBefore(actionStub)).toEqual(true);
  350. expect(notifyUpdateListenersSpy.calledAfter(actionStub)).toEqual(true);
  351. expect(notifyUpdateListenersSpy.getCall(0).args[0]).toEqual('beforeTriggerAction');
  352. expect(notifyUpdateListenersSpy.getCall(0).args[1]).toEqual({
  353. action: fileActions.getActions('all', OCA.Files.FileActions.TYPE_INLINE, OC.PERMISSION_READ)['Test'],
  354. fileName: 'Test.txt',
  355. context: context
  356. });
  357. expect(notifyUpdateListenersSpy.getCall(1).args[0]).toEqual('afterTriggerAction');
  358. expect(notifyUpdateListenersSpy.getCall(1).args[1]).toEqual({
  359. action: fileActions.getActions('all', OCA.Files.FileActions.TYPE_INLINE, OC.PERMISSION_READ)['Test'],
  360. fileName: 'Test.txt',
  361. context: context
  362. });
  363. }),
  364. describe('actions menu', function() {
  365. it('shows actions menu inside row when clicking the menu trigger', function() {
  366. expect($tr.find('td.filename .fileActionsMenu').length).toEqual(0);
  367. $tr.find('.action-menu').click();
  368. expect($tr.find('td.filename .fileActionsMenu').length).toEqual(1);
  369. });
  370. it('shows highlight on current row', function() {
  371. $tr.find('.action-menu').click();
  372. expect($tr.hasClass('mouseOver')).toEqual(true);
  373. });
  374. it('cleans up after hiding', function() {
  375. var slideUpStub = sinon.stub($.fn, 'slideUp');
  376. $tr.find('.action-menu').click();
  377. expect($tr.find('.fileActionsMenu').length).toEqual(1);
  378. OC.hideMenus();
  379. // sliding animation
  380. expect(slideUpStub.calledOnce).toEqual(true);
  381. slideUpStub.getCall(0).args[1]();
  382. expect($tr.hasClass('mouseOver')).toEqual(false);
  383. expect($tr.find('.fileActionsMenu').length).toEqual(0);
  384. });
  385. });
  386. });
  387. describe('custom rendering', function() {
  388. var $tr;
  389. beforeEach(function() {
  390. var fileData = {
  391. id: 18,
  392. type: 'file',
  393. name: 'testName.txt',
  394. mimetype: 'text/plain',
  395. size: '1234',
  396. etag: 'a01234c',
  397. mtime: '123456'
  398. };
  399. $tr = fileList.add(fileData);
  400. });
  401. it('regular function', function() {
  402. var actionStub = sinon.stub();
  403. fileActions.registerAction({
  404. name: 'Test',
  405. displayName: '',
  406. mime: 'all',
  407. type: OCA.Files.FileActions.TYPE_INLINE,
  408. permissions: OC.PERMISSION_READ,
  409. render: function(actionSpec, isDefault, context) {
  410. expect(actionSpec.name).toEqual('Test');
  411. expect(actionSpec.displayName).toEqual('');
  412. expect(actionSpec.permissions).toEqual(OC.PERMISSION_READ);
  413. expect(actionSpec.mime).toEqual('all');
  414. expect(isDefault).toEqual(false);
  415. expect(context.fileList).toEqual(fileList);
  416. expect(context.$file[0]).toEqual($tr[0]);
  417. var $customEl = $('<a class="action action-test" href="#"><span>blabli</span><span>blabla</span></a>');
  418. $tr.find('td:first').append($customEl);
  419. return $customEl;
  420. },
  421. actionHandler: actionStub
  422. });
  423. fileActions.display($tr.find('td.filename'), true, fileList);
  424. var $actionEl = $tr.find('td:first .action-test');
  425. expect($actionEl.length).toEqual(1);
  426. expect($actionEl.hasClass('action')).toEqual(true);
  427. $actionEl.click();
  428. expect(actionStub.calledOnce).toEqual(true);
  429. expect(actionStub.getCall(0).args[0]).toEqual('testName.txt');
  430. });
  431. });
  432. describe('merging', function() {
  433. var $tr;
  434. beforeEach(function() {
  435. var fileData = {
  436. id: 18,
  437. type: 'file',
  438. name: 'testName.txt',
  439. path: '/anotherpath/there',
  440. mimetype: 'text/plain',
  441. size: '1234',
  442. etag: 'a01234c',
  443. mtime: '123456'
  444. };
  445. $tr = fileList.add(fileData);
  446. });
  447. afterEach(function() {
  448. $tr = null;
  449. });
  450. it('copies all actions to target file actions', function() {
  451. var actions1 = new OCA.Files.FileActions();
  452. var actions2 = new OCA.Files.FileActions();
  453. var actionStub1 = sinon.stub();
  454. var actionStub2 = sinon.stub();
  455. actions1.registerAction({
  456. name: 'Test',
  457. type: OCA.Files.FileActions.TYPE_INLINE,
  458. mime: 'all',
  459. permissions: OC.PERMISSION_READ,
  460. icon: OC.imagePath('core', 'actions/test'),
  461. actionHandler: actionStub1
  462. });
  463. actions2.registerAction({
  464. name: 'Test2',
  465. type: OCA.Files.FileActions.TYPE_INLINE,
  466. mime: 'all',
  467. permissions: OC.PERMISSION_READ,
  468. icon: OC.imagePath('core', 'actions/test'),
  469. actionHandler: actionStub2
  470. });
  471. actions2.merge(actions1);
  472. actions2.display($tr.find('td.filename'), true, fileList);
  473. expect($tr.find('.action-test').length).toEqual(1);
  474. expect($tr.find('.action-test2').length).toEqual(1);
  475. $tr.find('.action-test').click();
  476. expect(actionStub1.calledOnce).toEqual(true);
  477. expect(actionStub2.notCalled).toEqual(true);
  478. actionStub1.reset();
  479. $tr.find('.action-test2').click();
  480. expect(actionStub1.notCalled).toEqual(true);
  481. expect(actionStub2.calledOnce).toEqual(true);
  482. });
  483. it('overrides existing actions on merge', function() {
  484. var actions1 = new OCA.Files.FileActions();
  485. var actions2 = new OCA.Files.FileActions();
  486. var actionStub1 = sinon.stub();
  487. var actionStub2 = sinon.stub();
  488. actions1.registerAction({
  489. name: 'Test',
  490. type: OCA.Files.FileActions.TYPE_INLINE,
  491. mime: 'all',
  492. permissions: OC.PERMISSION_READ,
  493. icon: OC.imagePath('core', 'actions/test'),
  494. actionHandler: actionStub1
  495. });
  496. actions2.registerAction({
  497. name: 'Test', // override
  498. mime: 'all',
  499. type: OCA.Files.FileActions.TYPE_INLINE,
  500. permissions: OC.PERMISSION_READ,
  501. icon: OC.imagePath('core', 'actions/test'),
  502. actionHandler: actionStub2
  503. });
  504. actions1.merge(actions2);
  505. actions1.display($tr.find('td.filename'), true, fileList);
  506. expect($tr.find('.action-test').length).toEqual(1);
  507. $tr.find('.action-test').click();
  508. expect(actionStub1.notCalled).toEqual(true);
  509. expect(actionStub2.calledOnce).toEqual(true);
  510. });
  511. it('overrides existing action when calling register after merge', function() {
  512. var actions1 = new OCA.Files.FileActions();
  513. var actions2 = new OCA.Files.FileActions();
  514. var actionStub1 = sinon.stub();
  515. var actionStub2 = sinon.stub();
  516. actions1.registerAction({
  517. mime: 'all',
  518. name: 'Test',
  519. type: OCA.Files.FileActions.TYPE_INLINE,
  520. permissions: OC.PERMISSION_READ,
  521. icon: OC.imagePath('core', 'actions/test'),
  522. actionHandler: actionStub1
  523. });
  524. actions1.merge(actions2);
  525. // late override
  526. actions1.registerAction({
  527. mime: 'all',
  528. name: 'Test', // override
  529. type: OCA.Files.FileActions.TYPE_INLINE,
  530. permissions: OC.PERMISSION_READ,
  531. icon: OC.imagePath('core', 'actions/test'),
  532. actionHandler: actionStub2
  533. });
  534. actions1.display($tr.find('td.filename'), true, fileList);
  535. expect($tr.find('.action-test').length).toEqual(1);
  536. $tr.find('.action-test').click();
  537. expect(actionStub1.notCalled).toEqual(true);
  538. expect(actionStub2.calledOnce).toEqual(true);
  539. });
  540. it('leaves original file actions untouched (clean copy)', function() {
  541. var actions1 = new OCA.Files.FileActions();
  542. var actions2 = new OCA.Files.FileActions();
  543. var actionStub1 = sinon.stub();
  544. var actionStub2 = sinon.stub();
  545. actions1.registerAction({
  546. mime: 'all',
  547. name: 'Test',
  548. type: OCA.Files.FileActions.TYPE_INLINE,
  549. permissions: OC.PERMISSION_READ,
  550. icon: OC.imagePath('core', 'actions/test'),
  551. actionHandler: actionStub1
  552. });
  553. // copy the Test action to actions2
  554. actions2.merge(actions1);
  555. // late override
  556. actions2.registerAction({
  557. mime: 'all',
  558. name: 'Test', // override
  559. type: OCA.Files.FileActions.TYPE_INLINE,
  560. permissions: OC.PERMISSION_READ,
  561. icon: OC.imagePath('core', 'actions/test'),
  562. actionHandler: actionStub2
  563. });
  564. // check if original actions still call the correct handler
  565. actions1.display($tr.find('td.filename'), true, fileList);
  566. expect($tr.find('.action-test').length).toEqual(1);
  567. $tr.find('.action-test').click();
  568. expect(actionStub1.calledOnce).toEqual(true);
  569. expect(actionStub2.notCalled).toEqual(true);
  570. });
  571. });
  572. describe('events', function() {
  573. var clock;
  574. beforeEach(function() {
  575. clock = sinon.useFakeTimers();
  576. });
  577. afterEach(function() {
  578. clock.restore();
  579. });
  580. it('notifies update event handlers once after multiple changes', function() {
  581. var actionStub = sinon.stub();
  582. var handler = sinon.stub();
  583. fileActions.on('registerAction', handler);
  584. fileActions.registerAction({
  585. mime: 'all',
  586. name: 'Test',
  587. type: OCA.Files.FileActions.TYPE_INLINE,
  588. permissions: OC.PERMISSION_READ,
  589. icon: OC.imagePath('core', 'actions/test'),
  590. actionHandler: actionStub
  591. });
  592. fileActions.registerAction({
  593. mime: 'all',
  594. name: 'Test2',
  595. permissions: OC.PERMISSION_READ,
  596. icon: OC.imagePath('core', 'actions/test'),
  597. actionHandler: actionStub
  598. });
  599. expect(handler.calledTwice).toEqual(true);
  600. });
  601. it('does not notifies update event handlers after unregistering', function() {
  602. var actionStub = sinon.stub();
  603. var handler = sinon.stub();
  604. fileActions.on('registerAction', handler);
  605. fileActions.off('registerAction', handler);
  606. fileActions.registerAction({
  607. mime: 'all',
  608. name: 'Test',
  609. type: OCA.Files.FileActions.TYPE_INLINE,
  610. permissions: OC.PERMISSION_READ,
  611. icon: OC.imagePath('core', 'actions/test'),
  612. actionHandler: actionStub
  613. });
  614. fileActions.registerAction({
  615. mime: 'all',
  616. name: 'Test2',
  617. type: OCA.Files.FileActions.TYPE_INLINE,
  618. permissions: OC.PERMISSION_READ,
  619. icon: OC.imagePath('core', 'actions/test'),
  620. actionHandler: actionStub
  621. });
  622. expect(handler.notCalled).toEqual(true);
  623. });
  624. });
  625. describe('default actions', function() {
  626. describe('download', function() {
  627. it('redirects to URL and sets busy state to list', function() {
  628. var handleDownloadStub = sinon.stub(OCA.Files.Files, 'handleDownload');
  629. var busyStub = sinon.stub(fileList, 'showFileBusyState');
  630. var fileData = {
  631. id: 18,
  632. type: 'file',
  633. name: 'testName.txt',
  634. mimetype: 'text/plain',
  635. size: '1234',
  636. etag: 'a01234c',
  637. mtime: '123456',
  638. permissions: OC.PERMISSION_READ | OC.PERMISSION_UPDATE
  639. };
  640. // note: FileActions.display() is called implicitly
  641. fileList.add(fileData);
  642. var model = fileList.getModelForFile('testName.txt');
  643. fileActions.registerDefaultActions();
  644. fileActions.triggerAction('Download', model, fileList);
  645. expect(busyStub.calledOnce).toEqual(true);
  646. expect(busyStub.calledWith('testName.txt', true)).toEqual(true);
  647. expect(handleDownloadStub.calledOnce).toEqual(true);
  648. expect(handleDownloadStub.getCall(0).args[0]).toEqual(
  649. OC.getRootPath() + '/remote.php/webdav/subdir/testName.txt'
  650. );
  651. busyStub.reset();
  652. handleDownloadStub.yield();
  653. expect(busyStub.calledOnce).toEqual(true);
  654. expect(busyStub.calledWith('testName.txt', false)).toEqual(true);
  655. busyStub.restore();
  656. handleDownloadStub.restore();
  657. });
  658. });
  659. });
  660. describe('download spinner', function() {
  661. var FileActions = OCA.Files.FileActions;
  662. var $el;
  663. beforeEach(function() {
  664. $el = $('<a href="#"><span class="icon icon-download"></span><span>Download</span></a>');
  665. });
  666. it('replaces download icon with spinner', function() {
  667. FileActions.updateFileActionSpinner($el, true);
  668. expect($el.find('.icon.icon-loading-small').length).toEqual(1);
  669. expect($el.find('.icon.icon-download').hasClass('hidden')).toEqual(true);
  670. });
  671. it('replaces spinner back with download icon with spinner', function() {
  672. FileActions.updateFileActionSpinner($el, true);
  673. FileActions.updateFileActionSpinner($el, false);
  674. expect($el.find('.icon.icon-loading-small').length).toEqual(0);
  675. expect($el.find('.icon.icon-download').hasClass('hidden')).toEqual(false);
  676. });
  677. });
  678. });