appSpec.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. /**
  2. * ownCloud
  3. *
  4. * @author Vincent Petry
  5. * @copyright 2014 Vincent Petry <pvince81@owncloud.com>
  6. *
  7. * This library is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
  9. * License as published by the Free Software Foundation; either
  10. * version 3 of the License, or any later version.
  11. *
  12. * This library is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
  16. *
  17. * You should have received a copy of the GNU Affero General Public
  18. * License along with this library. If not, see <http://www.gnu.org/licenses/>.
  19. *
  20. */
  21. describe('OCA.Files.App tests', function() {
  22. var App = OCA.Files.App;
  23. var pushStateStub;
  24. var replaceStateStub;
  25. var parseUrlQueryStub;
  26. var oldLegacyFileActions;
  27. beforeEach(function() {
  28. $('#testArea').append(
  29. '<div id="content" class="app-files">' +
  30. '<div id="app-navigation">' +
  31. '<ul><li data-id="files"><a>Files</a></li>' +
  32. '<li data-id="other"><a>Other</a></li>' +
  33. '</div>' +
  34. '<div id="app-content">' +
  35. '<div id="app-content-files" class="hidden">' +
  36. '</div>' +
  37. '<div id="app-content-other" class="hidden">' +
  38. '</div>' +
  39. '</div>' +
  40. '</div>' +
  41. '</div>'
  42. );
  43. oldLegacyFileActions = window.FileActions;
  44. window.FileActions = new OCA.Files.FileActions();
  45. OCA.Files.legacyFileActions = window.FileActions;
  46. OCA.Files.fileActions = new OCA.Files.FileActions();
  47. pushStateStub = sinon.stub(OC.Util.History, 'pushState');
  48. replaceStateStub = sinon.stub(OC.Util.History, 'replaceState');
  49. parseUrlQueryStub = sinon.stub(OC.Util.History, 'parseUrlQuery');
  50. parseUrlQueryStub.returns({});
  51. App.initialize();
  52. });
  53. afterEach(function() {
  54. App.destroy();
  55. window.FileActions = oldLegacyFileActions;
  56. pushStateStub.restore();
  57. replaceStateStub.restore();
  58. parseUrlQueryStub.restore();
  59. });
  60. describe('initialization', function() {
  61. it('initializes the default file list with the default file actions', function() {
  62. expect(App.fileList).toBeDefined();
  63. expect(App.fileList.fileActions.actions.all).toBeDefined();
  64. expect(App.fileList.$el.is('#app-content-files')).toEqual(true);
  65. });
  66. it('merges the legacy file actions with the default ones', function() {
  67. var legacyActionStub = sinon.stub();
  68. var actionStub = sinon.stub();
  69. // legacy action
  70. window.FileActions.register(
  71. 'all',
  72. 'LegacyTest',
  73. OC.PERMISSION_READ,
  74. OC.imagePath('core', 'actions/test'),
  75. legacyActionStub
  76. );
  77. // legacy action to be overwritten
  78. window.FileActions.register(
  79. 'all',
  80. 'OverwriteThis',
  81. OC.PERMISSION_READ,
  82. OC.imagePath('core', 'actions/test'),
  83. legacyActionStub
  84. );
  85. // regular file actions
  86. OCA.Files.fileActions.register(
  87. 'all',
  88. 'RegularTest',
  89. OC.PERMISSION_READ,
  90. OC.imagePath('core', 'actions/test'),
  91. actionStub
  92. );
  93. // overwrite
  94. OCA.Files.fileActions.register(
  95. 'all',
  96. 'OverwriteThis',
  97. OC.PERMISSION_READ,
  98. OC.imagePath('core', 'actions/test'),
  99. actionStub
  100. );
  101. App.initialize();
  102. var actions = App.fileList.fileActions.actions;
  103. var context = { fileActions: sinon.createStubInstance(OCA.Files.FileActions) };
  104. actions.all.OverwriteThis.action('testFileName', context);
  105. expect(actionStub.calledOnce).toBe(true);
  106. expect(context.fileActions._notifyUpdateListeners.callCount).toBe(2);
  107. expect(context.fileActions._notifyUpdateListeners.getCall(0).calledWith('beforeTriggerAction')).toBe(true);
  108. expect(context.fileActions._notifyUpdateListeners.getCall(1).calledWith('afterTriggerAction')).toBe(true);
  109. actions.all.LegacyTest.action('testFileName', context);
  110. expect(legacyActionStub.calledOnce).toBe(true);
  111. expect(context.fileActions._notifyUpdateListeners.callCount).toBe(4);
  112. expect(context.fileActions._notifyUpdateListeners.getCall(2).calledWith('beforeTriggerAction')).toBe(true);
  113. expect(context.fileActions._notifyUpdateListeners.getCall(3).calledWith('afterTriggerAction')).toBe(true);
  114. actions.all.RegularTest.action('testFileName', context);
  115. expect(actionStub.calledTwice).toBe(true);
  116. expect(context.fileActions._notifyUpdateListeners.callCount).toBe(6);
  117. expect(context.fileActions._notifyUpdateListeners.getCall(4).calledWith('beforeTriggerAction')).toBe(true);
  118. expect(context.fileActions._notifyUpdateListeners.getCall(5).calledWith('afterTriggerAction')).toBe(true);
  119. // default one still there
  120. expect(actions.dir.Open.action).toBeDefined();
  121. });
  122. });
  123. describe('URL handling', function() {
  124. it('pushes the state to the URL when current app changed directory', function() {
  125. $('#app-content-files').trigger(new $.Event('changeDirectory', {dir: 'sub dir'}));
  126. expect(pushStateStub.calledOnce).toEqual(true);
  127. var params = OC.parseQueryString(pushStateStub.getCall(0).args[0]);
  128. expect(params.dir).toEqual('sub dir');
  129. expect(params.view).not.toBeDefined();
  130. $('li[data-id=other]>a').click();
  131. pushStateStub.reset();
  132. $('#app-content-other').trigger(new $.Event('changeDirectory', {dir: 'sub dir'}));
  133. expect(pushStateStub.calledOnce).toEqual(true);
  134. params = OC.parseQueryString(pushStateStub.getCall(0).args[0]);
  135. expect(params.dir).toEqual('sub dir');
  136. expect(params.view).toEqual('other');
  137. });
  138. it('replaces the state to the URL when fileid is known', function() {
  139. $('#app-content-files').trigger(new $.Event('changeDirectory', {dir: 'sub dir'}));
  140. expect(pushStateStub.calledOnce).toEqual(true);
  141. var params = OC.parseQueryString(pushStateStub.getCall(0).args[0]);
  142. expect(params.dir).toEqual('sub dir');
  143. expect(params.view).not.toBeDefined();
  144. expect(replaceStateStub.notCalled).toEqual(true);
  145. parseUrlQueryStub.returns({dir: 'sub dir'});
  146. $('#app-content-files').trigger(new $.Event('afterChangeDirectory', {dir: 'sub dir', fileId: 123}));
  147. expect(pushStateStub.calledOnce).toEqual(true);
  148. expect(replaceStateStub.calledOnce).toEqual(true);
  149. params = OC.parseQueryString(replaceStateStub.getCall(0).args[0]);
  150. expect(params.dir).toEqual('sub dir');
  151. expect(params.view).not.toBeDefined();
  152. expect(params.fileid).toEqual('123');
  153. });
  154. describe('onpopstate', function() {
  155. it('sends "urlChanged" event to current app', function() {
  156. var handler = sinon.stub();
  157. $('#app-content-files').on('urlChanged', handler);
  158. App._onPopState({view: 'files', dir: '/somedir'});
  159. expect(handler.calledOnce).toEqual(true);
  160. expect(handler.getCall(0).args[0].view).toEqual('files');
  161. expect(handler.getCall(0).args[0].dir).toEqual('/somedir');
  162. });
  163. it('sends "show" event to current app and sets navigation', function() {
  164. var showHandlerFiles = sinon.stub();
  165. var showHandlerOther = sinon.stub();
  166. var hideHandlerFiles = sinon.stub();
  167. var hideHandlerOther = sinon.stub();
  168. $('#app-content-files').on('show', showHandlerFiles);
  169. $('#app-content-files').on('hide', hideHandlerFiles);
  170. $('#app-content-other').on('show', showHandlerOther);
  171. $('#app-content-other').on('hide', hideHandlerOther);
  172. App._onPopState({view: 'other', dir: '/somedir'});
  173. expect(showHandlerFiles.notCalled).toEqual(true);
  174. expect(hideHandlerFiles.calledOnce).toEqual(true);
  175. expect(showHandlerOther.calledOnce).toEqual(true);
  176. expect(hideHandlerOther.notCalled).toEqual(true);
  177. showHandlerFiles.reset();
  178. showHandlerOther.reset();
  179. hideHandlerFiles.reset();
  180. hideHandlerOther.reset();
  181. App._onPopState({view: 'files', dir: '/somedir'});
  182. expect(showHandlerFiles.calledOnce).toEqual(true);
  183. expect(hideHandlerFiles.notCalled).toEqual(true);
  184. expect(showHandlerOther.notCalled).toEqual(true);
  185. expect(hideHandlerOther.calledOnce).toEqual(true);
  186. expect(App.navigation.getActiveItem()).toEqual('files');
  187. expect($('#app-content-files').hasClass('hidden')).toEqual(false);
  188. expect($('#app-content-other').hasClass('hidden')).toEqual(true);
  189. });
  190. it('does not send "show" or "hide" event to current app when already visible', function() {
  191. var showHandler = sinon.stub();
  192. var hideHandler = sinon.stub();
  193. $('#app-content-files').on('show', showHandler);
  194. $('#app-content-files').on('hide', hideHandler);
  195. App._onPopState({view: 'files', dir: '/somedir'});
  196. expect(showHandler.notCalled).toEqual(true);
  197. expect(hideHandler.notCalled).toEqual(true);
  198. });
  199. it('state defaults to files app with root dir', function() {
  200. var handler = sinon.stub();
  201. parseUrlQueryStub.returns({});
  202. $('#app-content-files').on('urlChanged', handler);
  203. App._onPopState();
  204. expect(handler.calledOnce).toEqual(true);
  205. expect(handler.getCall(0).args[0].view).toEqual('files');
  206. expect(handler.getCall(0).args[0].dir).toEqual('/');
  207. });
  208. it('activates files app if invalid view is passed', function() {
  209. App._onPopState({view: 'invalid', dir: '/somedir'});
  210. expect(App.navigation.getActiveItem()).toEqual('files');
  211. expect($('#app-content-files').hasClass('hidden')).toEqual(false);
  212. });
  213. });
  214. describe('navigation', function() {
  215. it('switches the navigation item and panel visibility when onpopstate', function() {
  216. App._onPopState({view: 'other', dir: '/somedir'});
  217. expect(App.navigation.getActiveItem()).toEqual('other');
  218. expect($('#app-content-files').hasClass('hidden')).toEqual(true);
  219. expect($('#app-content-other').hasClass('hidden')).toEqual(false);
  220. expect($('li[data-id=files] > a').hasClass('active')).toEqual(false);
  221. expect($('li[data-id=other] > a').hasClass('active')).toEqual(true);
  222. App._onPopState({view: 'files', dir: '/somedir'});
  223. expect(App.navigation.getActiveItem()).toEqual('files');
  224. expect($('#app-content-files').hasClass('hidden')).toEqual(false);
  225. expect($('#app-content-other').hasClass('hidden')).toEqual(true);
  226. expect($('li[data-id=files] > a').hasClass('active')).toEqual(true);
  227. expect($('li[data-id=other] > a').hasClass('active')).toEqual(false);
  228. });
  229. it('clicking on navigation switches the panel visibility', function() {
  230. $('li[data-id=other] > a').click();
  231. expect(App.navigation.getActiveItem()).toEqual('other');
  232. expect($('#app-content-files').hasClass('hidden')).toEqual(true);
  233. expect($('#app-content-other').hasClass('hidden')).toEqual(false);
  234. expect($('li[data-id=files] > a').hasClass('active')).toEqual(false);
  235. expect($('li[data-id=other] > a').hasClass('active')).toEqual(true);
  236. $('li[data-id=files] > a').click();
  237. expect(App.navigation.getActiveItem()).toEqual('files');
  238. expect($('#app-content-files').hasClass('hidden')).toEqual(false);
  239. expect($('#app-content-other').hasClass('hidden')).toEqual(true);
  240. expect($('li[data-id=files] > a').hasClass('active')).toEqual(true);
  241. expect($('li[data-id=other] > a').hasClass('active')).toEqual(false);
  242. });
  243. it('clicking on navigation sends "show" and "urlChanged" event', function() {
  244. var handler = sinon.stub();
  245. var showHandler = sinon.stub();
  246. $('#app-content-other').on('urlChanged', handler);
  247. $('#app-content-other').on('show', showHandler);
  248. $('li[data-id=other] > a').click();
  249. expect(handler.calledOnce).toEqual(true);
  250. expect(handler.getCall(0).args[0].view).toEqual('other');
  251. expect(handler.getCall(0).args[0].dir).toEqual('/');
  252. expect(showHandler.calledOnce).toEqual(true);
  253. });
  254. it('clicking on activate navigation only sends "urlChanged" event', function() {
  255. var handler = sinon.stub();
  256. var showHandler = sinon.stub();
  257. $('#app-content-files').on('urlChanged', handler);
  258. $('#app-content-files').on('show', showHandler);
  259. $('li[data-id=files] > a').click();
  260. expect(handler.calledOnce).toEqual(true);
  261. expect(handler.getCall(0).args[0].view).toEqual('files');
  262. expect(handler.getCall(0).args[0].dir).toEqual('/');
  263. expect(showHandler.notCalled).toEqual(true);
  264. });
  265. });
  266. describe('viewer mode', function() {
  267. it('toggles the sidebar when viewer mode is enabled', function() {
  268. $('#app-content-files').trigger(
  269. new $.Event('changeViewerMode', {viewerModeEnabled: true}
  270. ));
  271. expect($('#app-navigation').hasClass('hidden')).toEqual(true);
  272. expect($('.app-files').hasClass('viewer-mode no-sidebar')).toEqual(true);
  273. $('#app-content-files').trigger(
  274. new $.Event('changeViewerMode', {viewerModeEnabled: false}
  275. ));
  276. expect($('#app-navigation').hasClass('hidden')).toEqual(false);
  277. expect($('.app-files').hasClass('viewer-mode no-sidebar')).toEqual(false);
  278. });
  279. });
  280. });
  281. });