filelistSpec.js 95 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615
  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.FileList tests', function() {
  22. var FileInfo = OC.Files.FileInfo;
  23. var testFiles, testRoot, notificationStub, fileList, pageSizeStub;
  24. var bcResizeStub;
  25. var filesClient;
  26. var redirectStub;
  27. /**
  28. * Generate test file data
  29. */
  30. function generateFiles(startIndex, endIndex) {
  31. var files = [];
  32. var name;
  33. for (var i = startIndex; i <= endIndex; i++) {
  34. name = 'File with index ';
  35. if (i < 10) {
  36. // do not rely on localeCompare here
  37. // and make the sorting predictable
  38. // cross-browser
  39. name += '0';
  40. }
  41. name += i + '.txt';
  42. files.push(new FileInfo({
  43. id: i,
  44. type: 'file',
  45. name: name,
  46. mimetype: 'text/plain',
  47. size: i * 2,
  48. etag: 'abc'
  49. }));
  50. }
  51. return files;
  52. }
  53. beforeEach(function() {
  54. filesClient = new OC.Files.Client({
  55. host: 'localhost',
  56. port: 80,
  57. // FIXME: uncomment after fixing the test OC.webroot
  58. //root: OC.webroot + '/remote.php/webdav',
  59. root: '/remote.php/webdav',
  60. useHTTPS: false
  61. });
  62. redirectStub = sinon.stub(OC, 'redirect');
  63. notificationStub = sinon.stub(OC.Notification, 'showTemporary');
  64. // prevent resize algo to mess up breadcrumb order while
  65. // testing
  66. bcResizeStub = sinon.stub(OCA.Files.BreadCrumb.prototype, '_resize');
  67. // init parameters and test table elements
  68. $('#testArea').append(
  69. '<div id="app-content-files">' +
  70. // init horrible parameters
  71. '<input type="hidden" id="dir" value="/subdir"/>' +
  72. '<input type="hidden" id="permissions" value="31"/>' +
  73. // dummy controls
  74. '<div id="controls">' +
  75. ' <div class="actions creatable"></div>' +
  76. ' <div class="notCreatable"></div>' +
  77. '</div>' +
  78. // uploader
  79. '<input type="file" id="file_upload_start" name="files[]" multiple="multiple">' +
  80. // dummy table
  81. // TODO: at some point this will be rendered by the fileList class itself!
  82. '<table id="filestable">' +
  83. '<thead><tr>' +
  84. '<th id="headerName" class="hidden column-name">' +
  85. '<input type="checkbox" id="select_all_files" class="select-all checkbox">' +
  86. '<a class="name columntitle" data-sort="name"><span>Name</span><span class="sort-indicator"></span></a>' +
  87. '<span id="selectedActionsList" class="selectedActions hidden">' +
  88. '<a href class="download"><img src="actions/download.svg">Download</a>' +
  89. '<a href class="delete-selected">Delete</a></span>' +
  90. '</th>' +
  91. '<th class="hidden column-size"><a class="columntitle" data-sort="size"><span class="sort-indicator"></span></a></th>' +
  92. '<th class="hidden column-mtime"><a class="columntitle" data-sort="mtime"><span class="sort-indicator"></span></a></th>' +
  93. '</tr></thead>' +
  94. '<tbody id="fileList"></tbody>' +
  95. '<tfoot></tfoot>' +
  96. '</table>' +
  97. // TODO: move to handlebars template
  98. '<div id="emptycontent"><h2>Empty content message</h2><p class="uploadmessage">Upload message</p></div>' +
  99. '<div class="nofilterresults hidden"></div>' +
  100. '</div>'
  101. );
  102. testRoot = new FileInfo({
  103. // root entry
  104. id: 99,
  105. type: 'dir',
  106. name: '/subdir',
  107. mimetype: 'httpd/unix-directory',
  108. size: 1200000,
  109. etag: 'a0b0c0d0',
  110. permissions: OC.PERMISSION_ALL
  111. });
  112. testFiles = [new FileInfo({
  113. id: 1,
  114. type: 'file',
  115. name: 'One.txt',
  116. mimetype: 'text/plain',
  117. mtime: 123456789,
  118. size: 12,
  119. etag: 'abc',
  120. permissions: OC.PERMISSION_ALL
  121. }), new FileInfo({
  122. id: 2,
  123. type: 'file',
  124. name: 'Two.jpg',
  125. mimetype: 'image/jpeg',
  126. mtime: 234567890,
  127. size: 12049,
  128. etag: 'def',
  129. permissions: OC.PERMISSION_ALL
  130. }), new FileInfo({
  131. id: 3,
  132. type: 'file',
  133. name: 'Three.pdf',
  134. mimetype: 'application/pdf',
  135. mtime: 234560000,
  136. size: 58009,
  137. etag: '123',
  138. permissions: OC.PERMISSION_ALL
  139. }), new FileInfo({
  140. id: 4,
  141. type: 'dir',
  142. name: 'somedir',
  143. mimetype: 'httpd/unix-directory',
  144. mtime: 134560000,
  145. size: 250,
  146. etag: '456',
  147. permissions: OC.PERMISSION_ALL
  148. })];
  149. pageSizeStub = sinon.stub(OCA.Files.FileList.prototype, 'pageSize').returns(20);
  150. fileList = new OCA.Files.FileList($('#app-content-files'), {
  151. filesClient: filesClient
  152. });
  153. });
  154. afterEach(function() {
  155. testFiles = undefined;
  156. if (fileList) {
  157. fileList.destroy();
  158. }
  159. fileList = undefined;
  160. notificationStub.restore();
  161. bcResizeStub.restore();
  162. pageSizeStub.restore();
  163. redirectStub.restore();
  164. });
  165. describe('Getters', function() {
  166. it('Returns the current directory', function() {
  167. $('#dir').val('/one/two/three');
  168. expect(fileList.getCurrentDirectory()).toEqual('/one/two/three');
  169. });
  170. it('Returns the directory permissions as int', function() {
  171. $('#permissions').val('23');
  172. expect(fileList.getDirectoryPermissions()).toEqual(23);
  173. });
  174. });
  175. describe('Adding files', function() {
  176. var clock, now;
  177. beforeEach(function() {
  178. // to prevent date comparison issues
  179. clock = sinon.useFakeTimers();
  180. now = new Date();
  181. });
  182. afterEach(function() {
  183. clock.restore();
  184. });
  185. it('generates file element with correct attributes when calling add() with file data', function() {
  186. var fileData = new FileInfo({
  187. id: 18,
  188. name: 'testName.txt',
  189. mimetype: 'text/plain',
  190. size: 1234,
  191. etag: 'a01234c',
  192. mtime: 123456
  193. });
  194. var $tr = fileList.add(fileData);
  195. expect($tr).toBeDefined();
  196. expect($tr[0].tagName.toLowerCase()).toEqual('tr');
  197. expect($tr.attr('data-id')).toEqual('18');
  198. expect($tr.attr('data-type')).toEqual('file');
  199. expect($tr.attr('data-file')).toEqual('testName.txt');
  200. expect($tr.attr('data-size')).toEqual('1234');
  201. expect($tr.attr('data-etag')).toEqual('a01234c');
  202. expect($tr.attr('data-permissions')).toEqual('31');
  203. expect($tr.attr('data-mime')).toEqual('text/plain');
  204. expect($tr.attr('data-mtime')).toEqual('123456');
  205. expect($tr.find('a.name').attr('href'))
  206. .toEqual(OC.webroot + '/remote.php/webdav/subdir/testName.txt');
  207. expect($tr.find('.nametext').text().trim()).toEqual('testName.txt');
  208. expect($tr.find('.filesize').text()).toEqual('1 KB');
  209. expect($tr.find('.date').text()).not.toEqual('?');
  210. expect(fileList.findFileEl('testName.txt')[0]).toEqual($tr[0]);
  211. });
  212. it('generates dir element with correct attributes when calling add() with dir data', function() {
  213. var fileData = new FileInfo({
  214. id: 19,
  215. name: 'testFolder',
  216. mimetype: 'httpd/unix-directory',
  217. size: 1234,
  218. etag: 'a01234c',
  219. mtime: 123456
  220. });
  221. var $tr = fileList.add(fileData);
  222. expect($tr).toBeDefined();
  223. expect($tr[0].tagName.toLowerCase()).toEqual('tr');
  224. expect($tr.attr('data-id')).toEqual('19');
  225. expect($tr.attr('data-type')).toEqual('dir');
  226. expect($tr.attr('data-file')).toEqual('testFolder');
  227. expect($tr.attr('data-size')).toEqual('1234');
  228. expect($tr.attr('data-etag')).toEqual('a01234c');
  229. expect($tr.attr('data-permissions')).toEqual('31');
  230. expect($tr.attr('data-mime')).toEqual('httpd/unix-directory');
  231. expect($tr.attr('data-mtime')).toEqual('123456');
  232. expect($tr.find('.filesize').text()).toEqual('1 KB');
  233. expect($tr.find('.date').text()).not.toEqual('?');
  234. expect(fileList.findFileEl('testFolder')[0]).toEqual($tr[0]);
  235. });
  236. it('generates file element with default attributes when calling add() with minimal data', function() {
  237. var fileData = {
  238. type: 'file',
  239. name: 'testFile.txt'
  240. };
  241. clock.tick(123456);
  242. var $tr = fileList.add(fileData);
  243. expect($tr).toBeDefined();
  244. expect($tr[0].tagName.toLowerCase()).toEqual('tr');
  245. expect($tr.attr('data-id')).toBeUndefined();
  246. expect($tr.attr('data-type')).toEqual('file');
  247. expect($tr.attr('data-file')).toEqual('testFile.txt');
  248. expect($tr.attr('data-size')).toBeUndefined();
  249. expect($tr.attr('data-etag')).toBeUndefined();
  250. expect($tr.attr('data-permissions')).toEqual('31');
  251. expect($tr.attr('data-mime')).toBeUndefined();
  252. expect($tr.attr('data-mtime')).toEqual('123456');
  253. expect($tr.find('.filesize').text()).toEqual('Pending');
  254. expect($tr.find('.date').text()).not.toEqual('?');
  255. });
  256. it('generates dir element with default attributes when calling add() with minimal data', function() {
  257. var fileData = {
  258. type: 'dir',
  259. name: 'testFolder'
  260. };
  261. clock.tick(123456);
  262. var $tr = fileList.add(fileData);
  263. expect($tr).toBeDefined();
  264. expect($tr[0].tagName.toLowerCase()).toEqual('tr');
  265. expect($tr.attr('data-id')).toBeUndefined();
  266. expect($tr.attr('data-type')).toEqual('dir');
  267. expect($tr.attr('data-file')).toEqual('testFolder');
  268. expect($tr.attr('data-size')).toBeUndefined();
  269. expect($tr.attr('data-etag')).toBeUndefined();
  270. expect($tr.attr('data-permissions')).toEqual('31');
  271. expect($tr.attr('data-mime')).toEqual('httpd/unix-directory');
  272. expect($tr.attr('data-mtime')).toEqual('123456');
  273. expect($tr.find('.filesize').text()).toEqual('Pending');
  274. expect($tr.find('.date').text()).not.toEqual('?');
  275. });
  276. it('generates file element with zero size when size is explicitly zero', function() {
  277. var fileData = {
  278. type: 'dir',
  279. name: 'testFolder',
  280. size: '0'
  281. };
  282. var $tr = fileList.add(fileData);
  283. expect($tr.find('.filesize').text()).toEqual('0 KB');
  284. });
  285. it('generates file element with unknown date when mtime invalid', function() {
  286. var fileData = {
  287. type: 'dir',
  288. name: 'testFolder',
  289. mtime: -1
  290. };
  291. var $tr = fileList.add(fileData);
  292. expect($tr.find('.date .modified').text()).toEqual('?');
  293. });
  294. it('adds new file to the end of the list', function() {
  295. var $tr;
  296. var fileData = {
  297. type: 'file',
  298. name: 'ZZZ.txt'
  299. };
  300. fileList.setFiles(testFiles);
  301. $tr = fileList.add(fileData);
  302. expect($tr.index()).toEqual(4);
  303. });
  304. it('inserts files in a sorted manner when insert option is enabled', function() {
  305. for (var i = 0; i < testFiles.length; i++) {
  306. fileList.add(testFiles[i]);
  307. }
  308. expect(fileList.files[0].name).toEqual('somedir');
  309. expect(fileList.files[1].name).toEqual('One.txt');
  310. expect(fileList.files[2].name).toEqual('Three.pdf');
  311. expect(fileList.files[3].name).toEqual('Two.jpg');
  312. });
  313. it('inserts new file at correct position', function() {
  314. var $tr;
  315. var fileData = {
  316. type: 'file',
  317. name: 'P comes after O.txt'
  318. };
  319. for (var i = 0; i < testFiles.length; i++) {
  320. fileList.add(testFiles[i]);
  321. }
  322. $tr = fileList.add(fileData);
  323. // after "One.txt"
  324. expect($tr.index()).toEqual(2);
  325. expect(fileList.files[2]).toEqual(fileData);
  326. });
  327. it('inserts new folder at correct position in insert mode', function() {
  328. var $tr;
  329. var fileData = {
  330. type: 'dir',
  331. name: 'somedir2 comes after somedir'
  332. };
  333. for (var i = 0; i < testFiles.length; i++) {
  334. fileList.add(testFiles[i]);
  335. }
  336. $tr = fileList.add(fileData);
  337. expect($tr.index()).toEqual(1);
  338. expect(fileList.files[1]).toEqual(fileData);
  339. });
  340. it('inserts new file at the end correctly', function() {
  341. var $tr;
  342. var fileData = {
  343. type: 'file',
  344. name: 'zzz.txt'
  345. };
  346. for (var i = 0; i < testFiles.length; i++) {
  347. fileList.add(testFiles[i]);
  348. }
  349. $tr = fileList.add(fileData);
  350. expect($tr.index()).toEqual(4);
  351. expect(fileList.files[4]).toEqual(fileData);
  352. });
  353. it('removes empty content message and shows summary when adding first file', function() {
  354. var $summary;
  355. var fileData = {
  356. type: 'file',
  357. name: 'first file.txt',
  358. size: 12
  359. };
  360. fileList.setFiles([]);
  361. expect(fileList.isEmpty).toEqual(true);
  362. fileList.add(fileData);
  363. $summary = $('#filestable .summary');
  364. expect($summary.hasClass('hidden')).toEqual(false);
  365. // yes, ugly...
  366. expect($summary.find('.info').text()).toEqual('0 folders and 1 file');
  367. expect($summary.find('.dirinfo').hasClass('hidden')).toEqual(true);
  368. expect($summary.find('.fileinfo').hasClass('hidden')).toEqual(false);
  369. expect($summary.find('.filesize').text()).toEqual('12 B');
  370. expect($('#filestable thead th').hasClass('hidden')).toEqual(false);
  371. expect($('#emptycontent').hasClass('hidden')).toEqual(true);
  372. expect(fileList.isEmpty).toEqual(false);
  373. });
  374. it('correctly adds the extension markup and show hidden files completely in gray', function() {
  375. var $tr;
  376. var testDataAndExpectedResult = [
  377. {file: {type: 'file', name: 'ZZZ.txt'}, extension: '.txt'},
  378. {file: {type: 'file', name: 'ZZZ.tar.gz'}, extension: '.gz'},
  379. {file: {type: 'file', name: 'test.with.some.dots.in.it.txt'}, extension: '.txt'},
  380. // we render hidden files completely in gray
  381. {file: {type: 'file', name: '.test.with.some.dots.in.it.txt'}, extension: '.test.with.some.dots.in.it.txt'},
  382. {file: {type: 'file', name: '.hidden'}, extension: '.hidden'},
  383. ];
  384. fileList.setFiles(testFiles);
  385. for(var i = 0; i < testDataAndExpectedResult.length; i++) {
  386. var testSet = testDataAndExpectedResult[i];
  387. var fileData = testSet['file'];
  388. $tr = fileList.add(fileData);
  389. expect($tr.find('.nametext .extension').text()).toEqual(testSet['extension']);
  390. }
  391. });
  392. });
  393. describe('Removing files from the list', function() {
  394. it('Removes file from list when calling remove() and updates summary', function() {
  395. var $summary;
  396. var $removedEl;
  397. fileList.setFiles(testFiles);
  398. $removedEl = fileList.remove('One.txt');
  399. expect($removedEl).toBeDefined();
  400. expect($removedEl.attr('data-file')).toEqual('One.txt');
  401. expect($('#fileList tr').length).toEqual(3);
  402. expect(fileList.files.length).toEqual(3);
  403. expect(fileList.findFileEl('One.txt').length).toEqual(0);
  404. $summary = $('#filestable .summary');
  405. expect($summary.hasClass('hidden')).toEqual(false);
  406. expect($summary.find('.info').text()).toEqual('1 folder and 2 files');
  407. expect($summary.find('.dirinfo').hasClass('hidden')).toEqual(false);
  408. expect($summary.find('.fileinfo').hasClass('hidden')).toEqual(false);
  409. expect($summary.find('.filesize').text()).toEqual('69 KB');
  410. expect(fileList.isEmpty).toEqual(false);
  411. });
  412. it('Shows empty content when removing last file', function() {
  413. var $summary;
  414. fileList.setFiles([testFiles[0]]);
  415. fileList.remove('One.txt');
  416. expect($('#fileList tr').length).toEqual(0);
  417. expect(fileList.files.length).toEqual(0);
  418. expect(fileList.findFileEl('One.txt').length).toEqual(0);
  419. $summary = $('#filestable .summary');
  420. expect($summary.hasClass('hidden')).toEqual(true);
  421. expect($('#filestable thead th').hasClass('hidden')).toEqual(true);
  422. expect($('#emptycontent').hasClass('hidden')).toEqual(false);
  423. expect(fileList.isEmpty).toEqual(true);
  424. });
  425. });
  426. describe('Deleting files', function() {
  427. var deferredDelete;
  428. var deleteStub;
  429. beforeEach(function() {
  430. deferredDelete = $.Deferred();
  431. deleteStub = sinon.stub(filesClient, 'remove').returns(deferredDelete.promise());
  432. });
  433. afterEach(function() {
  434. deleteStub.restore();
  435. });
  436. function doDelete() {
  437. // note: normally called from FileActions
  438. fileList.do_delete(['One.txt', 'Two.jpg']);
  439. expect(deleteStub.calledTwice).toEqual(true);
  440. expect(deleteStub.getCall(0).args[0]).toEqual('/subdir/One.txt');
  441. expect(deleteStub.getCall(1).args[0]).toEqual('/subdir/Two.jpg');
  442. }
  443. it('calls delete.php, removes the deleted entries and updates summary', function() {
  444. var $summary;
  445. fileList.setFiles(testFiles);
  446. doDelete();
  447. deferredDelete.resolve(200);
  448. expect(fileList.findFileEl('One.txt').length).toEqual(0);
  449. expect(fileList.findFileEl('Two.jpg').length).toEqual(0);
  450. expect(fileList.findFileEl('Three.pdf').length).toEqual(1);
  451. expect(fileList.$fileList.find('tr').length).toEqual(2);
  452. $summary = $('#filestable .summary');
  453. expect($summary.hasClass('hidden')).toEqual(false);
  454. expect($summary.find('.info').text()).toEqual('1 folder and 1 file');
  455. expect($summary.find('.dirinfo').hasClass('hidden')).toEqual(false);
  456. expect($summary.find('.fileinfo').hasClass('hidden')).toEqual(false);
  457. expect($summary.find('.filesize').text()).toEqual('57 KB');
  458. expect(fileList.isEmpty).toEqual(false);
  459. expect($('#filestable thead th').hasClass('hidden')).toEqual(false);
  460. expect($('#emptycontent').hasClass('hidden')).toEqual(true);
  461. expect(notificationStub.notCalled).toEqual(true);
  462. });
  463. it('shows busy state on files to be deleted', function() {
  464. fileList.setFiles(testFiles);
  465. doDelete();
  466. expect(fileList.findFileEl('One.txt').hasClass('busy')).toEqual(true);
  467. expect(fileList.findFileEl('Three.pdf').hasClass('busy')).toEqual(false);
  468. });
  469. it('shows busy state on all files when deleting all', function() {
  470. fileList.setFiles(testFiles);
  471. fileList.do_delete();
  472. expect(fileList.$fileList.find('tr.busy').length).toEqual(4);
  473. });
  474. it('updates summary when deleting last file', function() {
  475. var $summary;
  476. fileList.setFiles([testFiles[0], testFiles[1]]);
  477. doDelete();
  478. deferredDelete.resolve(200);
  479. expect(fileList.$fileList.find('tr').length).toEqual(0);
  480. $summary = $('#filestable .summary');
  481. expect($summary.hasClass('hidden')).toEqual(true);
  482. expect(fileList.isEmpty).toEqual(true);
  483. expect(fileList.files.length).toEqual(0);
  484. expect($('#filestable thead th').hasClass('hidden')).toEqual(true);
  485. expect($('#emptycontent').hasClass('hidden')).toEqual(false);
  486. });
  487. it('bring back deleted item when delete call failed', function() {
  488. fileList.setFiles(testFiles);
  489. doDelete();
  490. deferredDelete.reject(403);
  491. // files are still in the list
  492. expect(fileList.findFileEl('One.txt').length).toEqual(1);
  493. expect(fileList.findFileEl('Two.jpg').length).toEqual(1);
  494. expect(fileList.$fileList.find('tr').length).toEqual(4);
  495. expect(notificationStub.calledTwice).toEqual(true);
  496. });
  497. it('remove file from list if delete call returned 404 not found', function() {
  498. fileList.setFiles(testFiles);
  499. doDelete();
  500. deferredDelete.reject(404);
  501. // files are still in the list
  502. expect(fileList.findFileEl('One.txt').length).toEqual(0);
  503. expect(fileList.findFileEl('Two.jpg').length).toEqual(0);
  504. expect(fileList.$fileList.find('tr').length).toEqual(2);
  505. expect(notificationStub.notCalled).toEqual(true);
  506. });
  507. });
  508. describe('Renaming files', function() {
  509. var deferredRename;
  510. var renameStub;
  511. beforeEach(function() {
  512. deferredRename = $.Deferred();
  513. renameStub = sinon.stub(filesClient, 'move').returns(deferredRename.promise());
  514. });
  515. afterEach(function() {
  516. renameStub.restore();
  517. });
  518. function doCancelRename() {
  519. var $input;
  520. for (var i = 0; i < testFiles.length; i++) {
  521. fileList.add(testFiles[i]);
  522. }
  523. // trigger rename prompt
  524. fileList.rename('One.txt');
  525. $input = fileList.$fileList.find('input.filename');
  526. // keep same name
  527. $input.val('One.txt');
  528. // trigger submit because triggering blur doesn't work in all browsers
  529. $input.closest('form').trigger('submit');
  530. expect(renameStub.notCalled).toEqual(true);
  531. }
  532. function doRename() {
  533. var $input;
  534. for (var i = 0; i < testFiles.length; i++) {
  535. var file = testFiles[i];
  536. file.path = '/some/subdir';
  537. fileList.add(file, {silent: true});
  538. }
  539. // trigger rename prompt
  540. fileList.rename('One.txt');
  541. $input = fileList.$fileList.find('input.filename');
  542. $input.val('Tu_after_three.txt');
  543. // trigger submit because triggering blur doesn't work in all browsers
  544. $input.closest('form').trigger('submit');
  545. expect(renameStub.calledOnce).toEqual(true);
  546. expect(renameStub.getCall(0).args[0]).toEqual('/some/subdir/One.txt');
  547. expect(renameStub.getCall(0).args[1]).toEqual('/some/subdir/Tu_after_three.txt');
  548. }
  549. it('Inserts renamed file entry at correct position if rename ajax call suceeded', function() {
  550. doRename();
  551. deferredRename.resolve(201);
  552. // element stays renamed
  553. expect(fileList.findFileEl('One.txt').length).toEqual(0);
  554. expect(fileList.findFileEl('Tu_after_three.txt').length).toEqual(1);
  555. expect(fileList.findFileEl('Tu_after_three.txt').index()).toEqual(2); // after Two.jpg
  556. expect(notificationStub.notCalled).toEqual(true);
  557. });
  558. it('Reverts file entry if rename ajax call failed', function() {
  559. doRename();
  560. deferredRename.reject(403);
  561. // element was reverted
  562. expect(fileList.findFileEl('One.txt').length).toEqual(1);
  563. expect(fileList.findFileEl('One.txt').index()).toEqual(1); // after somedir
  564. expect(fileList.findFileEl('Tu_after_three.txt').length).toEqual(0);
  565. expect(notificationStub.calledOnce).toEqual(true);
  566. });
  567. it('Correctly updates file link after rename', function() {
  568. var $tr;
  569. doRename();
  570. deferredRename.resolve(201);
  571. $tr = fileList.findFileEl('Tu_after_three.txt');
  572. expect($tr.find('a.name').attr('href'))
  573. .toEqual(OC.webroot + '/remote.php/webdav/some/subdir/Tu_after_three.txt');
  574. });
  575. it('Triggers "fileActionsReady" event after rename', function() {
  576. var handler = sinon.stub();
  577. fileList.$fileList.on('fileActionsReady', handler);
  578. doRename();
  579. expect(handler.notCalled).toEqual(true);
  580. deferredRename.resolve(201);
  581. expect(handler.calledOnce).toEqual(true);
  582. expect(fileList.$fileList.find('.test').length).toEqual(0);
  583. });
  584. it('Leaves the summary alone when reinserting renamed element', function() {
  585. var $summary = $('#filestable .summary');
  586. doRename();
  587. deferredRename.resolve(201);
  588. expect($summary.find('.info').text()).toEqual('1 folder and 3 files');
  589. });
  590. it('Leaves the summary alone when cancel renaming', function() {
  591. var $summary = $('#filestable .summary');
  592. doCancelRename();
  593. expect($summary.find('.info').text()).toEqual('1 folder and 3 files');
  594. });
  595. it('Shows busy state while rename in progress', function() {
  596. var $tr;
  597. doRename();
  598. // element is renamed before the request finishes
  599. $tr = fileList.findFileEl('Tu_after_three.txt');
  600. expect($tr.length).toEqual(1);
  601. expect(fileList.findFileEl('One.txt').length).toEqual(0);
  602. // file actions are hidden
  603. expect($tr.hasClass('busy')).toEqual(true);
  604. // input and form are gone
  605. expect(fileList.$fileList.find('input.filename').length).toEqual(0);
  606. expect(fileList.$fileList.find('form').length).toEqual(0);
  607. });
  608. it('Validates the file name', function() {
  609. var $input, $tr;
  610. for (var i = 0; i < testFiles.length; i++) {
  611. fileList.add(testFiles[i], {silent: true});
  612. }
  613. // trigger rename prompt
  614. fileList.rename('One.txt');
  615. $input = fileList.$fileList.find('input.filename');
  616. $input.val('Two.jpg');
  617. // simulate key to trigger validation
  618. $input.trigger(new $.Event('keyup', {keyCode: 97}));
  619. // input is still there with error
  620. expect(fileList.$fileList.find('input.filename').length).toEqual(1);
  621. expect(fileList.$fileList.find('input.filename').hasClass('error')).toEqual(true);
  622. // trigger submit does not send server request
  623. $input.closest('form').trigger('submit');
  624. expect(renameStub.notCalled).toEqual(true);
  625. // simulate escape key
  626. $input.trigger(new $.Event('keyup', {keyCode: 27}));
  627. // element is added back with the correct name
  628. $tr = fileList.findFileEl('One.txt');
  629. expect($tr.length).toEqual(1);
  630. expect($tr.find('a .nametext').text().trim()).toEqual('One.txt');
  631. expect($tr.find('a.name').is(':visible')).toEqual(true);
  632. $tr = fileList.findFileEl('Two.jpg');
  633. expect($tr.length).toEqual(1);
  634. expect($tr.find('a .nametext').text().trim()).toEqual('Two.jpg');
  635. expect($tr.find('a.name').is(':visible')).toEqual(true);
  636. // input and form are gone
  637. expect(fileList.$fileList.find('input.filename').length).toEqual(0);
  638. expect(fileList.$fileList.find('form').length).toEqual(0);
  639. });
  640. it('Restores thumbnail when rename was cancelled', function() {
  641. doRename();
  642. expect(OC.TestUtil.getImageUrl(fileList.findFileEl('Tu_after_three.txt').find('.thumbnail')))
  643. .toEqual(OC.imagePath('core', 'loading.gif'));
  644. deferredRename.reject(409);
  645. expect(fileList.findFileEl('One.txt').length).toEqual(1);
  646. expect(OC.TestUtil.getImageUrl(fileList.findFileEl('One.txt').find('.thumbnail')))
  647. .toEqual(OC.imagePath('core', 'filetypes/text.svg'));
  648. });
  649. });
  650. describe('Moving files', function() {
  651. var deferredMove;
  652. var moveStub;
  653. beforeEach(function() {
  654. deferredMove = $.Deferred();
  655. moveStub = sinon.stub(filesClient, 'move').returns(deferredMove.promise());
  656. fileList.setFiles(testFiles);
  657. });
  658. afterEach(function() {
  659. moveStub.restore();
  660. });
  661. it('Moves single file to target folder', function() {
  662. fileList.move('One.txt', '/somedir');
  663. expect(moveStub.calledOnce).toEqual(true);
  664. expect(moveStub.getCall(0).args[0]).toEqual('/subdir/One.txt');
  665. expect(moveStub.getCall(0).args[1]).toEqual('/somedir/One.txt');
  666. deferredMove.resolve(201);
  667. expect(fileList.findFileEl('One.txt').length).toEqual(0);
  668. // folder size has increased
  669. expect(fileList.findFileEl('somedir').data('size')).toEqual(262);
  670. expect(fileList.findFileEl('somedir').find('.filesize').text()).toEqual('262 B');
  671. expect(notificationStub.notCalled).toEqual(true);
  672. });
  673. it('Moves list of files to target folder', function() {
  674. var deferredMove1 = $.Deferred();
  675. var deferredMove2 = $.Deferred();
  676. moveStub.onCall(0).returns(deferredMove1.promise());
  677. moveStub.onCall(1).returns(deferredMove2.promise());
  678. fileList.move(['One.txt', 'Two.jpg'], '/somedir');
  679. expect(moveStub.calledTwice).toEqual(true);
  680. expect(moveStub.getCall(0).args[0]).toEqual('/subdir/One.txt');
  681. expect(moveStub.getCall(0).args[1]).toEqual('/somedir/One.txt');
  682. expect(moveStub.getCall(1).args[0]).toEqual('/subdir/Two.jpg');
  683. expect(moveStub.getCall(1).args[1]).toEqual('/somedir/Two.jpg');
  684. deferredMove1.resolve(201);
  685. expect(fileList.findFileEl('One.txt').length).toEqual(0);
  686. // folder size has increased during move
  687. expect(fileList.findFileEl('somedir').data('size')).toEqual(262);
  688. expect(fileList.findFileEl('somedir').find('.filesize').text()).toEqual('262 B');
  689. deferredMove2.resolve(201);
  690. expect(fileList.findFileEl('Two.jpg').length).toEqual(0);
  691. // folder size has increased
  692. expect(fileList.findFileEl('somedir').data('size')).toEqual(12311);
  693. expect(fileList.findFileEl('somedir').find('.filesize').text()).toEqual('12 KB');
  694. expect(notificationStub.notCalled).toEqual(true);
  695. });
  696. it('Shows notification if a file could not be moved', function() {
  697. fileList.move('One.txt', '/somedir');
  698. expect(moveStub.calledOnce).toEqual(true);
  699. deferredMove.reject(409);
  700. expect(fileList.findFileEl('One.txt').length).toEqual(1);
  701. expect(notificationStub.calledOnce).toEqual(true);
  702. expect(notificationStub.getCall(0).args[0]).toEqual('Could not move "One.txt"');
  703. });
  704. it('Restores thumbnail if a file could not be moved', function() {
  705. fileList.move('One.txt', '/somedir');
  706. expect(OC.TestUtil.getImageUrl(fileList.findFileEl('One.txt').find('.thumbnail')))
  707. .toEqual(OC.imagePath('core', 'loading.gif'));
  708. expect(moveStub.calledOnce).toEqual(true);
  709. deferredMove.reject(409);
  710. expect(fileList.findFileEl('One.txt').length).toEqual(1);
  711. expect(notificationStub.calledOnce).toEqual(true);
  712. expect(notificationStub.getCall(0).args[0]).toEqual('Could not move "One.txt"');
  713. expect(OC.TestUtil.getImageUrl(fileList.findFileEl('One.txt').find('.thumbnail')))
  714. .toEqual(OC.imagePath('core', 'filetypes/text.svg'));
  715. });
  716. });
  717. describe('Update file', function() {
  718. it('does not change summary', function() {
  719. var $summary = $('#filestable .summary');
  720. var fileData = new FileInfo({
  721. type: 'file',
  722. name: 'test file',
  723. });
  724. var $tr = fileList.add(fileData);
  725. expect($summary.find('.info').text()).toEqual('0 folders and 1 file');
  726. var model = fileList.getModelForFile('test file');
  727. model.set({size: '100'});
  728. expect($summary.find('.info').text()).toEqual('0 folders and 1 file');
  729. });
  730. })
  731. describe('List rendering', function() {
  732. it('renders a list of files using add()', function() {
  733. expect(fileList.files.length).toEqual(0);
  734. expect(fileList.files).toEqual([]);
  735. fileList.setFiles(testFiles);
  736. expect($('#fileList tr').length).toEqual(4);
  737. expect(fileList.files.length).toEqual(4);
  738. expect(fileList.files).toEqual(testFiles);
  739. });
  740. it('updates summary using the file sizes', function() {
  741. var $summary;
  742. fileList.setFiles(testFiles);
  743. $summary = $('#filestable .summary');
  744. expect($summary.hasClass('hidden')).toEqual(false);
  745. expect($summary.find('.info').text()).toEqual('1 folder and 3 files');
  746. expect($summary.find('.filesize').text()).toEqual('69 KB');
  747. });
  748. it('shows headers, summary and hide empty content message after setting files', function(){
  749. fileList.setFiles(testFiles);
  750. expect($('#filestable thead th').hasClass('hidden')).toEqual(false);
  751. expect($('#emptycontent').hasClass('hidden')).toEqual(true);
  752. expect(fileList.$el.find('.summary').hasClass('hidden')).toEqual(false);
  753. });
  754. it('hides headers, summary and show empty content message after setting empty file list', function(){
  755. fileList.setFiles([]);
  756. expect($('#filestable thead th').hasClass('hidden')).toEqual(true);
  757. expect($('#emptycontent').hasClass('hidden')).toEqual(false);
  758. expect($('#emptycontent .uploadmessage').hasClass('hidden')).toEqual(false);
  759. expect(fileList.$el.find('.summary').hasClass('hidden')).toEqual(true);
  760. });
  761. it('hides headers, upload message, and summary when list is empty and user has no creation permission', function(){
  762. $('#permissions').val(0);
  763. fileList.setFiles([]);
  764. expect($('#filestable thead th').hasClass('hidden')).toEqual(true);
  765. expect($('#emptycontent').hasClass('hidden')).toEqual(false);
  766. expect($('#emptycontent .uploadmessage').hasClass('hidden')).toEqual(true);
  767. expect(fileList.$el.find('.summary').hasClass('hidden')).toEqual(true);
  768. });
  769. it('calling findFileEl() can find existing file element', function() {
  770. fileList.setFiles(testFiles);
  771. expect(fileList.findFileEl('Two.jpg').length).toEqual(1);
  772. });
  773. it('calling findFileEl() returns empty when file not found in file', function() {
  774. fileList.setFiles(testFiles);
  775. expect(fileList.findFileEl('unexist.dat').length).toEqual(0);
  776. });
  777. it('only add file if in same current directory', function() {
  778. $('#dir').val('/current dir');
  779. var fileData = {
  780. type: 'file',
  781. name: 'testFile.txt',
  782. directory: '/current dir'
  783. };
  784. fileList.add(fileData);
  785. expect(fileList.findFileEl('testFile.txt').length).toEqual(1);
  786. });
  787. it('triggers "fileActionsReady" event after update', function() {
  788. var handler = sinon.stub();
  789. fileList.$fileList.on('fileActionsReady', handler);
  790. fileList.setFiles(testFiles);
  791. expect(handler.calledOnce).toEqual(true);
  792. expect(handler.getCall(0).args[0].$files.length).toEqual(testFiles.length);
  793. });
  794. it('triggers "fileActionsReady" event after single add', function() {
  795. var handler = sinon.stub();
  796. var $tr;
  797. fileList.setFiles(testFiles);
  798. fileList.$fileList.on('fileActionsReady', handler);
  799. $tr = fileList.add({name: 'test.txt'});
  800. expect(handler.calledOnce).toEqual(true);
  801. expect(handler.getCall(0).args[0].$files.is($tr)).toEqual(true);
  802. });
  803. it('triggers "fileActionsReady" event after next page load with the newly appended files', function() {
  804. var handler = sinon.stub();
  805. fileList.setFiles(generateFiles(0, 64));
  806. fileList.$fileList.on('fileActionsReady', handler);
  807. fileList._nextPage();
  808. expect(handler.calledOnce).toEqual(true);
  809. expect(handler.getCall(0).args[0].$files.length).toEqual(fileList.pageSize());
  810. });
  811. it('does not trigger "fileActionsReady" event after single add with silent argument', function() {
  812. var handler = sinon.stub();
  813. fileList.setFiles(testFiles);
  814. fileList.$fileList.on('fileActionsReady', handler);
  815. fileList.add({name: 'test.txt'}, {silent: true});
  816. expect(handler.notCalled).toEqual(true);
  817. });
  818. it('triggers "updated" event after update', function() {
  819. var handler = sinon.stub();
  820. fileList.$fileList.on('updated', handler);
  821. fileList.setFiles(testFiles);
  822. expect(handler.calledOnce).toEqual(true);
  823. });
  824. it('does not update summary when removing non-existing files', function() {
  825. var $summary;
  826. // single file
  827. fileList.setFiles([testFiles[0]]);
  828. $summary = $('#filestable .summary');
  829. expect($summary.hasClass('hidden')).toEqual(false);
  830. expect($summary.find('.info').text()).toEqual('0 folders and 1 file');
  831. fileList.remove('unexist.txt');
  832. expect($summary.hasClass('hidden')).toEqual(false);
  833. expect($summary.find('.info').text()).toEqual('0 folders and 1 file');
  834. });
  835. });
  836. describe('Filtered list rendering', function() {
  837. it('filters the list of files using filter()', function() {
  838. expect(fileList.files.length).toEqual(0);
  839. expect(fileList.files).toEqual([]);
  840. fileList.setFiles(testFiles);
  841. var $summary = $('#filestable .summary');
  842. var $nofilterresults = fileList.$el.find(".nofilterresults");
  843. expect($nofilterresults.length).toEqual(1);
  844. expect($summary.hasClass('hidden')).toEqual(false);
  845. expect($('#fileList tr:not(.hidden)').length).toEqual(4);
  846. expect(fileList.files.length).toEqual(4);
  847. expect($summary.hasClass('hidden')).toEqual(false);
  848. expect($nofilterresults.hasClass('hidden')).toEqual(true);
  849. fileList.setFilter('e');
  850. expect($('#fileList tr:not(.hidden)').length).toEqual(3);
  851. expect(fileList.files.length).toEqual(4);
  852. expect($summary.hasClass('hidden')).toEqual(false);
  853. expect($summary.find('.info').text()).toEqual("1 folder and 2 files match 'e'");
  854. expect($nofilterresults.hasClass('hidden')).toEqual(true);
  855. fileList.setFilter('ee');
  856. expect($('#fileList tr:not(.hidden)').length).toEqual(1);
  857. expect(fileList.files.length).toEqual(4);
  858. expect($summary.hasClass('hidden')).toEqual(false);
  859. expect($summary.find('.info').text()).toEqual("0 folders and 1 file matches 'ee'");
  860. expect($nofilterresults.hasClass('hidden')).toEqual(true);
  861. fileList.setFilter('eee');
  862. expect($('#fileList tr:not(.hidden)').length).toEqual(0);
  863. expect(fileList.files.length).toEqual(4);
  864. expect($summary.hasClass('hidden')).toEqual(true);
  865. expect($nofilterresults.hasClass('hidden')).toEqual(false);
  866. fileList.setFilter('ee');
  867. expect($('#fileList tr:not(.hidden)').length).toEqual(1);
  868. expect(fileList.files.length).toEqual(4);
  869. expect($summary.hasClass('hidden')).toEqual(false);
  870. expect($summary.find('.info').text()).toEqual("0 folders and 1 file matches 'ee'");
  871. expect($nofilterresults.hasClass('hidden')).toEqual(true);
  872. fileList.setFilter('e');
  873. expect($('#fileList tr:not(.hidden)').length).toEqual(3);
  874. expect(fileList.files.length).toEqual(4);
  875. expect($summary.hasClass('hidden')).toEqual(false);
  876. expect($summary.find('.info').text()).toEqual("1 folder and 2 files match 'e'");
  877. expect($nofilterresults.hasClass('hidden')).toEqual(true);
  878. fileList.setFilter('');
  879. expect($('#fileList tr:not(.hidden)').length).toEqual(4);
  880. expect(fileList.files.length).toEqual(4);
  881. expect($summary.hasClass('hidden')).toEqual(false);
  882. expect($summary.find('.info').text()).toEqual("1 folder and 3 files");
  883. expect($nofilterresults.hasClass('hidden')).toEqual(true);
  884. });
  885. it('hides the emptyfiles notice when using filter()', function() {
  886. expect(fileList.files.length).toEqual(0);
  887. expect(fileList.files).toEqual([]);
  888. fileList.setFiles([]);
  889. var $summary = $('#filestable .summary');
  890. var $emptycontent = fileList.$el.find("#emptycontent");
  891. var $nofilterresults = fileList.$el.find(".nofilterresults");
  892. expect($emptycontent.length).toEqual(1);
  893. expect($nofilterresults.length).toEqual(1);
  894. expect($('#fileList tr:not(.hidden)').length).toEqual(0);
  895. expect(fileList.files.length).toEqual(0);
  896. expect($summary.hasClass('hidden')).toEqual(true);
  897. expect($emptycontent.hasClass('hidden')).toEqual(false);
  898. expect($nofilterresults.hasClass('hidden')).toEqual(true);
  899. fileList.setFilter('e');
  900. expect($('#fileList tr:not(.hidden)').length).toEqual(0);
  901. expect(fileList.files.length).toEqual(0);
  902. expect($summary.hasClass('hidden')).toEqual(true);
  903. expect($emptycontent.hasClass('hidden')).toEqual(true);
  904. expect($nofilterresults.hasClass('hidden')).toEqual(false);
  905. fileList.setFilter('');
  906. expect($('#fileList tr:not(.hidden)').length).toEqual(0);
  907. expect(fileList.files.length).toEqual(0);
  908. expect($summary.hasClass('hidden')).toEqual(true);
  909. expect($emptycontent.hasClass('hidden')).toEqual(false);
  910. expect($nofilterresults.hasClass('hidden')).toEqual(true);
  911. });
  912. it('does not show the emptyfiles or nofilterresults notice when the mask is active', function() {
  913. expect(fileList.files.length).toEqual(0);
  914. expect(fileList.files).toEqual([]);
  915. fileList.showMask();
  916. fileList.setFiles(testFiles);
  917. var $emptycontent = fileList.$el.find("#emptycontent");
  918. var $nofilterresults = fileList.$el.find(".nofilterresults");
  919. expect($emptycontent.length).toEqual(1);
  920. expect($nofilterresults.length).toEqual(1);
  921. expect($emptycontent.hasClass('hidden')).toEqual(true);
  922. expect($nofilterresults.hasClass('hidden')).toEqual(true);
  923. /*
  924. fileList.setFilter('e');
  925. expect($emptycontent.hasClass('hidden')).toEqual(true);
  926. expect($nofilterresults.hasClass('hidden')).toEqual(false);
  927. */
  928. fileList.setFilter('');
  929. expect($emptycontent.hasClass('hidden')).toEqual(true);
  930. expect($nofilterresults.hasClass('hidden')).toEqual(true);
  931. });
  932. });
  933. describe('Rendering next page on scroll', function() {
  934. beforeEach(function() {
  935. fileList.setFiles(generateFiles(0, 64));
  936. });
  937. it('renders only the first page', function() {
  938. expect(fileList.files.length).toEqual(65);
  939. expect($('#fileList tr').length).toEqual(20);
  940. });
  941. it('renders the second page when scrolling down (trigger nextPage)', function() {
  942. // TODO: can't simulate scrolling here, so calling nextPage directly
  943. fileList._nextPage(true);
  944. expect($('#fileList tr').length).toEqual(40);
  945. fileList._nextPage(true);
  946. expect($('#fileList tr').length).toEqual(60);
  947. fileList._nextPage(true);
  948. expect($('#fileList tr').length).toEqual(65);
  949. fileList._nextPage(true);
  950. // stays at 65
  951. expect($('#fileList tr').length).toEqual(65);
  952. });
  953. it('inserts into the DOM if insertion point is in the visible page ', function() {
  954. fileList.add({
  955. id: 2000,
  956. type: 'file',
  957. name: 'File with index 15b.txt'
  958. });
  959. expect($('#fileList tr').length).toEqual(21);
  960. expect(fileList.findFileEl('File with index 15b.txt').index()).toEqual(16);
  961. });
  962. it('does not inserts into the DOM if insertion point is not the visible page ', function() {
  963. fileList.add({
  964. id: 2000,
  965. type: 'file',
  966. name: 'File with index 28b.txt'
  967. });
  968. expect($('#fileList tr').length).toEqual(20);
  969. expect(fileList.findFileEl('File with index 28b.txt').length).toEqual(0);
  970. fileList._nextPage(true);
  971. expect($('#fileList tr').length).toEqual(40);
  972. expect(fileList.findFileEl('File with index 28b.txt').index()).toEqual(29);
  973. });
  974. it('appends into the DOM when inserting a file after the last visible element', function() {
  975. fileList.add({
  976. id: 2000,
  977. type: 'file',
  978. name: 'File with index 19b.txt'
  979. });
  980. expect($('#fileList tr').length).toEqual(21);
  981. fileList._nextPage(true);
  982. expect($('#fileList tr').length).toEqual(41);
  983. });
  984. it('appends into the DOM when inserting a file on the last page when visible', function() {
  985. fileList._nextPage(true);
  986. expect($('#fileList tr').length).toEqual(40);
  987. fileList._nextPage(true);
  988. expect($('#fileList tr').length).toEqual(60);
  989. fileList._nextPage(true);
  990. expect($('#fileList tr').length).toEqual(65);
  991. fileList._nextPage(true);
  992. fileList.add({
  993. id: 2000,
  994. type: 'file',
  995. name: 'File with index 88.txt'
  996. });
  997. expect($('#fileList tr').length).toEqual(66);
  998. fileList._nextPage(true);
  999. expect($('#fileList tr').length).toEqual(66);
  1000. });
  1001. it('shows additional page when appending a page of files and scrolling down', function() {
  1002. var newFiles = generateFiles(66, 81);
  1003. for (var i = 0; i < newFiles.length; i++) {
  1004. fileList.add(newFiles[i]);
  1005. }
  1006. expect($('#fileList tr').length).toEqual(20);
  1007. fileList._nextPage(true);
  1008. expect($('#fileList tr').length).toEqual(40);
  1009. fileList._nextPage(true);
  1010. expect($('#fileList tr').length).toEqual(60);
  1011. fileList._nextPage(true);
  1012. expect($('#fileList tr').length).toEqual(80);
  1013. fileList._nextPage(true);
  1014. expect($('#fileList tr').length).toEqual(81);
  1015. fileList._nextPage(true);
  1016. expect($('#fileList tr').length).toEqual(81);
  1017. });
  1018. it('automatically renders next page when there are not enough elements visible', function() {
  1019. // delete the 15 first elements
  1020. for (var i = 0; i < 15; i++) {
  1021. fileList.remove(fileList.files[0].name);
  1022. }
  1023. // still makes sure that there are 20 elements visible, if any
  1024. expect($('#fileList tr').length).toEqual(25);
  1025. });
  1026. });
  1027. describe('file previews', function() {
  1028. var previewLoadStub;
  1029. beforeEach(function() {
  1030. previewLoadStub = sinon.stub(OCA.Files.FileList.prototype, 'lazyLoadPreview');
  1031. });
  1032. afterEach(function() {
  1033. previewLoadStub.restore();
  1034. });
  1035. it('renders default file icon when none provided and no mime type is set', function() {
  1036. var fileData = {
  1037. name: 'testFile.txt'
  1038. };
  1039. var $tr = fileList.add(fileData);
  1040. var $imgDiv = $tr.find('td.filename .thumbnail');
  1041. expect(OC.TestUtil.getImageUrl($imgDiv)).toEqual(OC.webroot + '/core/img/filetypes/file.svg');
  1042. // tries to load preview
  1043. expect(previewLoadStub.calledOnce).toEqual(true);
  1044. });
  1045. it('renders default icon for folder when none provided', function() {
  1046. var fileData = {
  1047. name: 'test dir',
  1048. mimetype: 'httpd/unix-directory'
  1049. };
  1050. var $tr = fileList.add(fileData);
  1051. var $imgDiv = $tr.find('td.filename .thumbnail');
  1052. expect(OC.TestUtil.getImageUrl($imgDiv)).toEqual(OC.webroot + '/core/img/filetypes/folder.svg');
  1053. // no preview since it's a directory
  1054. expect(previewLoadStub.notCalled).toEqual(true);
  1055. });
  1056. it('renders provided icon for file when provided', function() {
  1057. var fileData = new FileInfo({
  1058. type: 'file',
  1059. name: 'test file',
  1060. icon: OC.webroot + '/core/img/filetypes/application-pdf.svg',
  1061. mimetype: 'application/pdf'
  1062. });
  1063. var $tr = fileList.add(fileData);
  1064. var $imgDiv = $tr.find('td.filename .thumbnail');
  1065. expect(OC.TestUtil.getImageUrl($imgDiv)).toEqual(OC.webroot + '/core/img/filetypes/application-pdf.svg');
  1066. // try loading preview
  1067. expect(previewLoadStub.calledOnce).toEqual(true);
  1068. });
  1069. it('renders provided icon for file when provided', function() {
  1070. var fileData = new FileInfo({
  1071. name: 'somefile.pdf',
  1072. icon: OC.webroot + '/core/img/filetypes/application-pdf.svg'
  1073. });
  1074. var $tr = fileList.add(fileData);
  1075. var $imgDiv = $tr.find('td.filename .thumbnail');
  1076. expect(OC.TestUtil.getImageUrl($imgDiv)).toEqual(OC.webroot + '/core/img/filetypes/application-pdf.svg');
  1077. // try loading preview
  1078. expect(previewLoadStub.calledOnce).toEqual(true);
  1079. });
  1080. it('renders provided icon for folder when provided', function() {
  1081. var fileData = new FileInfo({
  1082. name: 'some folder',
  1083. mimetype: 'httpd/unix-directory',
  1084. icon: OC.webroot + '/core/img/filetypes/folder-alt.svg'
  1085. });
  1086. var $tr = fileList.add(fileData);
  1087. var $imgDiv = $tr.find('td.filename .thumbnail');
  1088. expect(OC.TestUtil.getImageUrl($imgDiv)).toEqual(OC.webroot + '/core/img/filetypes/folder-alt.svg');
  1089. // do not load preview for folders
  1090. expect(previewLoadStub.notCalled).toEqual(true);
  1091. });
  1092. it('renders preview when no icon was provided', function() {
  1093. var fileData = {
  1094. type: 'file',
  1095. name: 'test file'
  1096. };
  1097. var $tr = fileList.add(fileData);
  1098. var $td = $tr.find('td.filename');
  1099. expect(OC.TestUtil.getImageUrl($td.find('.thumbnail')))
  1100. .toEqual(OC.webroot + '/core/img/filetypes/file.svg');
  1101. expect(previewLoadStub.calledOnce).toEqual(true);
  1102. // third argument is callback
  1103. previewLoadStub.getCall(0).args[0].callback(OC.webroot + '/somepath.png');
  1104. expect(OC.TestUtil.getImageUrl($td.find('.thumbnail'))).toEqual(OC.webroot + '/somepath.png');
  1105. });
  1106. it('does not render preview for directories', function() {
  1107. var fileData = {
  1108. type: 'dir',
  1109. mimetype: 'httpd/unix-directory',
  1110. name: 'test dir'
  1111. };
  1112. var $tr = fileList.add(fileData);
  1113. var $td = $tr.find('td.filename');
  1114. expect(OC.TestUtil.getImageUrl($td.find('.thumbnail'))).toEqual(OC.webroot + '/core/img/filetypes/folder.svg');
  1115. expect(previewLoadStub.notCalled).toEqual(true);
  1116. });
  1117. it('render external storage icon for external storage root', function() {
  1118. var fileData = {
  1119. type: 'dir',
  1120. mimetype: 'httpd/unix-directory',
  1121. name: 'test dir',
  1122. mountType: 'external-root'
  1123. };
  1124. var $tr = fileList.add(fileData);
  1125. var $td = $tr.find('td.filename');
  1126. expect(OC.TestUtil.getImageUrl($td.find('.thumbnail'))).toEqual(OC.webroot + '/core/img/filetypes/folder-external.svg');
  1127. expect(previewLoadStub.notCalled).toEqual(true);
  1128. });
  1129. it('render external storage icon for external storage subdir', function() {
  1130. var fileData = {
  1131. type: 'dir',
  1132. mimetype: 'httpd/unix-directory',
  1133. name: 'test dir',
  1134. mountType: 'external'
  1135. };
  1136. var $tr = fileList.add(fileData);
  1137. var $td = $tr.find('td.filename');
  1138. expect(OC.TestUtil.getImageUrl($td.find('.thumbnail'))).toEqual(OC.webroot + '/core/img/filetypes/folder-external.svg');
  1139. expect(previewLoadStub.notCalled).toEqual(true);
  1140. // default icon override
  1141. expect($tr.attr('data-icon')).toEqual(OC.webroot + '/core/img/filetypes/folder-external.svg');
  1142. });
  1143. });
  1144. describe('viewer mode', function() {
  1145. it('enabling viewer mode hides files table and action buttons', function() {
  1146. fileList.setViewerMode(true);
  1147. expect($('#filestable').hasClass('hidden')).toEqual(true);
  1148. expect($('.actions').hasClass('hidden')).toEqual(true);
  1149. expect($('.notCreatable').hasClass('hidden')).toEqual(true);
  1150. });
  1151. it('disabling viewer mode restores files table and action buttons', function() {
  1152. fileList.setViewerMode(true);
  1153. fileList.setViewerMode(false);
  1154. expect($('#filestable').hasClass('hidden')).toEqual(false);
  1155. expect($('.actions').hasClass('hidden')).toEqual(false);
  1156. expect($('.notCreatable').hasClass('hidden')).toEqual(true);
  1157. });
  1158. it('disabling viewer mode restores files table and action buttons with correct permissions', function() {
  1159. $('#permissions').val(0);
  1160. fileList.setViewerMode(true);
  1161. fileList.setViewerMode(false);
  1162. expect($('#filestable').hasClass('hidden')).toEqual(false);
  1163. expect($('.actions').hasClass('hidden')).toEqual(true);
  1164. expect($('.notCreatable').hasClass('hidden')).toEqual(false);
  1165. });
  1166. it('toggling viewer mode triggers event', function() {
  1167. var handler = sinon.stub();
  1168. fileList.$el.on('changeViewerMode', handler);
  1169. fileList.setViewerMode(true);
  1170. expect(handler.calledOnce).toEqual(true);
  1171. expect(handler.getCall(0).args[0].viewerModeEnabled).toEqual(true);
  1172. handler.reset();
  1173. fileList.setViewerMode(false);
  1174. expect(handler.calledOnce).toEqual(true);
  1175. expect(handler.getCall(0).args[0].viewerModeEnabled).toEqual(false);
  1176. });
  1177. });
  1178. describe('loading file list', function() {
  1179. var deferredList;
  1180. var getFolderContentsStub;
  1181. beforeEach(function() {
  1182. deferredList = $.Deferred();
  1183. getFolderContentsStub = sinon.stub(filesClient, 'getFolderContents').returns(deferredList.promise());
  1184. });
  1185. afterEach(function() {
  1186. getFolderContentsStub.restore();
  1187. });
  1188. it('fetches file list from server and renders it when reload() is called', function() {
  1189. fileList.reload();
  1190. expect(getFolderContentsStub.calledOnce).toEqual(true);
  1191. expect(getFolderContentsStub.calledWith('/subdir')).toEqual(true);
  1192. deferredList.resolve(200, [testRoot].concat(testFiles));
  1193. expect($('#fileList tr').length).toEqual(4);
  1194. expect(fileList.findFileEl('One.txt').length).toEqual(1);
  1195. });
  1196. it('switches dir and fetches file list when calling changeDirectory()', function() {
  1197. fileList.changeDirectory('/anothersubdir');
  1198. expect(fileList.getCurrentDirectory()).toEqual('/anothersubdir');
  1199. expect(getFolderContentsStub.calledOnce).toEqual(true);
  1200. expect(getFolderContentsStub.calledWith('/anothersubdir')).toEqual(true);
  1201. });
  1202. it('converts backslashes to slashes when calling changeDirectory()', function() {
  1203. fileList.changeDirectory('/another\\subdir');
  1204. expect(fileList.getCurrentDirectory()).toEqual('/another/subdir');
  1205. });
  1206. it('switches to root dir when current directory does not exist', function() {
  1207. fileList.changeDirectory('/unexist');
  1208. deferredList.reject(404);
  1209. expect(fileList.getCurrentDirectory()).toEqual('/');
  1210. });
  1211. it('switches to root dir when current directory is forbidden', function() {
  1212. fileList.changeDirectory('/unexist');
  1213. deferredList.reject(403);
  1214. expect(fileList.getCurrentDirectory()).toEqual('/');
  1215. });
  1216. it('switches to root dir when current directory is unavailable', function() {
  1217. fileList.changeDirectory('/unexist');
  1218. deferredList.reject(500);
  1219. expect(fileList.getCurrentDirectory()).toEqual('/');
  1220. });
  1221. it('shows mask before loading file list then hides it at the end', function() {
  1222. var showMaskStub = sinon.stub(fileList, 'showMask');
  1223. var hideMaskStub = sinon.stub(fileList, 'hideMask');
  1224. fileList.changeDirectory('/anothersubdir');
  1225. expect(showMaskStub.calledOnce).toEqual(true);
  1226. expect(hideMaskStub.calledOnce).toEqual(false);
  1227. deferredList.resolve(200, [testRoot].concat(testFiles));
  1228. expect(showMaskStub.calledOnce).toEqual(true);
  1229. expect(hideMaskStub.calledOnce).toEqual(true);
  1230. showMaskStub.restore();
  1231. hideMaskStub.restore();
  1232. });
  1233. it('triggers "changeDirectory" event when changing directory', function() {
  1234. var handler = sinon.stub();
  1235. $('#app-content-files').on('changeDirectory', handler);
  1236. fileList.changeDirectory('/somedir');
  1237. deferredList.resolve(200, [testRoot].concat(testFiles));
  1238. expect(handler.calledOnce).toEqual(true);
  1239. expect(handler.getCall(0).args[0].dir).toEqual('/somedir');
  1240. });
  1241. it('changes the directory when receiving "urlChanged" event', function() {
  1242. $('#app-content-files').trigger(new $.Event('urlChanged', {view: 'files', dir: '/somedir'}));
  1243. expect(fileList.getCurrentDirectory()).toEqual('/somedir');
  1244. });
  1245. it('refreshes breadcrumb after update', function() {
  1246. var setDirSpy = sinon.spy(fileList.breadcrumb, 'setDirectory');
  1247. fileList.changeDirectory('/anothersubdir');
  1248. deferredList.resolve(200, [testRoot].concat(testFiles));
  1249. expect(fileList.breadcrumb.setDirectory.calledOnce).toEqual(true);
  1250. expect(fileList.breadcrumb.setDirectory.calledWith('/anothersubdir')).toEqual(true);
  1251. setDirSpy.restore();
  1252. getFolderContentsStub.restore();
  1253. });
  1254. });
  1255. describe('breadcrumb events', function() {
  1256. var deferredList;
  1257. var getFolderContentsStub;
  1258. beforeEach(function() {
  1259. deferredList = $.Deferred();
  1260. getFolderContentsStub = sinon.stub(filesClient, 'getFolderContents').returns(deferredList.promise());
  1261. });
  1262. afterEach(function() {
  1263. getFolderContentsStub.restore();
  1264. });
  1265. it('clicking on root breadcrumb changes directory to root', function() {
  1266. fileList.changeDirectory('/subdir/two/three with space/four/five');
  1267. deferredList.resolve(200, [testRoot].concat(testFiles));
  1268. var changeDirStub = sinon.stub(fileList, 'changeDirectory');
  1269. fileList.breadcrumb.$el.find('.crumb:eq(0)').trigger({type: 'click', which: 1});
  1270. expect(changeDirStub.calledOnce).toEqual(true);
  1271. expect(changeDirStub.getCall(0).args[0]).toEqual('/');
  1272. changeDirStub.restore();
  1273. });
  1274. it('clicking on breadcrumb changes directory', function() {
  1275. fileList.changeDirectory('/subdir/two/three with space/four/five');
  1276. deferredList.resolve(200, [testRoot].concat(testFiles));
  1277. var changeDirStub = sinon.stub(fileList, 'changeDirectory');
  1278. fileList.breadcrumb.$el.find('.crumb:eq(3)').trigger({type: 'click', which: 1});
  1279. expect(changeDirStub.calledOnce).toEqual(true);
  1280. expect(changeDirStub.getCall(0).args[0]).toEqual('/subdir/two/three with space');
  1281. changeDirStub.restore();
  1282. });
  1283. it('dropping files on breadcrumb calls move operation', function() {
  1284. var testDir = '/subdir/two/three with space/four/five';
  1285. var moveStub = sinon.stub(filesClient, 'move').returns($.Deferred().promise());
  1286. fileList.changeDirectory(testDir);
  1287. deferredList.resolve(200, [testRoot].concat(testFiles));
  1288. var $crumb = fileList.breadcrumb.$el.find('.crumb:eq(3)');
  1289. // no idea what this is but is required by the handler
  1290. var ui = {
  1291. helper: {
  1292. find: sinon.stub()
  1293. }
  1294. };
  1295. // returns a list of tr that were dragged
  1296. ui.helper.find.returns([
  1297. $('<tr data-file="One.txt" data-dir="' + testDir + '"></tr>'),
  1298. $('<tr data-file="Two.jpg" data-dir="' + testDir + '"></tr>')
  1299. ]);
  1300. // simulate drop event
  1301. fileList._onDropOnBreadCrumb(new $.Event('drop', {target: $crumb}), ui);
  1302. expect(moveStub.callCount).toEqual(2);
  1303. expect(moveStub.getCall(0).args[0]).toEqual(testDir + '/One.txt');
  1304. expect(moveStub.getCall(0).args[1]).toEqual('/subdir/two/three with space/One.txt');
  1305. expect(moveStub.getCall(1).args[0]).toEqual(testDir + '/Two.jpg');
  1306. expect(moveStub.getCall(1).args[1]).toEqual('/subdir/two/three with space/Two.jpg');
  1307. moveStub.restore();
  1308. });
  1309. it('dropping files on same dir breadcrumb does nothing', function() {
  1310. var testDir = '/subdir/two/three with space/four/five';
  1311. var moveStub = sinon.stub(filesClient, 'move').returns($.Deferred().promise());
  1312. fileList.changeDirectory(testDir);
  1313. deferredList.resolve(200, [testRoot].concat(testFiles));
  1314. var $crumb = fileList.breadcrumb.$el.find('.crumb:last');
  1315. // no idea what this is but is required by the handler
  1316. var ui = {
  1317. helper: {
  1318. find: sinon.stub()
  1319. }
  1320. };
  1321. // returns a list of tr that were dragged
  1322. ui.helper.find.returns([
  1323. $('<tr data-file="One.txt" data-dir="' + testDir + '"></tr>'),
  1324. $('<tr data-file="Two.jpg" data-dir="' + testDir + '"></tr>')
  1325. ]);
  1326. // simulate drop event
  1327. fileList._onDropOnBreadCrumb(new $.Event('drop', {target: $crumb}), ui);
  1328. // no extra server request
  1329. expect(moveStub.notCalled).toEqual(true);
  1330. });
  1331. });
  1332. describe('Download Url', function() {
  1333. it('returns correct download URL for single files', function() {
  1334. expect(fileList.getDownloadUrl('some file.txt'))
  1335. .toEqual(OC.webroot + '/remote.php/webdav/subdir/some%20file.txt');
  1336. expect(fileList.getDownloadUrl('some file.txt', '/anotherpath/abc'))
  1337. .toEqual(OC.webroot + '/remote.php/webdav/anotherpath/abc/some%20file.txt');
  1338. $('#dir').val('/');
  1339. expect(fileList.getDownloadUrl('some file.txt'))
  1340. .toEqual(OC.webroot + '/remote.php/webdav/some%20file.txt');
  1341. });
  1342. it('returns correct download URL for multiple files', function() {
  1343. expect(fileList.getDownloadUrl(['a b c.txt', 'd e f.txt']))
  1344. .toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2Fsubdir&files=%5B%22a%20b%20c.txt%22%2C%22d%20e%20f.txt%22%5D');
  1345. });
  1346. it('returns the correct ajax URL', function() {
  1347. expect(fileList.getAjaxUrl('test', {a:1, b:'x y'}))
  1348. .toEqual(OC.webroot + '/index.php/apps/files/ajax/test.php?a=1&b=x%20y');
  1349. });
  1350. });
  1351. describe('File selection', function() {
  1352. beforeEach(function() {
  1353. fileList.setFiles(testFiles);
  1354. });
  1355. it('Selects a file when clicking its checkbox', function() {
  1356. var $tr = fileList.findFileEl('One.txt');
  1357. expect($tr.find('input:checkbox').prop('checked')).toEqual(false);
  1358. $tr.find('td.filename input:checkbox').click();
  1359. expect($tr.find('input:checkbox').prop('checked')).toEqual(true);
  1360. });
  1361. it('Selects/deselect a file when clicking on the name while holding Ctrl', function() {
  1362. var $tr = fileList.findFileEl('One.txt');
  1363. var $tr2 = fileList.findFileEl('Three.pdf');
  1364. var e;
  1365. expect($tr.find('input:checkbox').prop('checked')).toEqual(false);
  1366. expect($tr2.find('input:checkbox').prop('checked')).toEqual(false);
  1367. e = new $.Event('click');
  1368. e.ctrlKey = true;
  1369. $tr.find('td.filename .name').trigger(e);
  1370. expect($tr.find('input:checkbox').prop('checked')).toEqual(true);
  1371. expect($tr2.find('input:checkbox').prop('checked')).toEqual(false);
  1372. // click on second entry, does not clear the selection
  1373. e = new $.Event('click');
  1374. e.ctrlKey = true;
  1375. $tr2.find('td.filename .name').trigger(e);
  1376. expect($tr.find('input:checkbox').prop('checked')).toEqual(true);
  1377. expect($tr2.find('input:checkbox').prop('checked')).toEqual(true);
  1378. expect(_.pluck(fileList.getSelectedFiles(), 'name')).toEqual(['One.txt', 'Three.pdf']);
  1379. // deselect now
  1380. e = new $.Event('click');
  1381. e.ctrlKey = true;
  1382. $tr2.find('td.filename .name').trigger(e);
  1383. expect($tr.find('input:checkbox').prop('checked')).toEqual(true);
  1384. expect($tr2.find('input:checkbox').prop('checked')).toEqual(false);
  1385. expect(_.pluck(fileList.getSelectedFiles(), 'name')).toEqual(['One.txt']);
  1386. });
  1387. it('Selects a range when clicking on one file then Shift clicking on another one', function() {
  1388. var $tr = fileList.findFileEl('One.txt');
  1389. var $tr2 = fileList.findFileEl('Three.pdf');
  1390. var e;
  1391. $tr.find('td.filename input:checkbox').click();
  1392. e = new $.Event('click');
  1393. e.shiftKey = true;
  1394. $tr2.find('td.filename .name').trigger(e);
  1395. expect($tr.find('input:checkbox').prop('checked')).toEqual(true);
  1396. expect($tr2.find('input:checkbox').prop('checked')).toEqual(true);
  1397. expect(fileList.findFileEl('Two.jpg').find('input:checkbox').prop('checked')).toEqual(true);
  1398. var selection = _.pluck(fileList.getSelectedFiles(), 'name');
  1399. expect(selection.length).toEqual(3);
  1400. expect(selection).toContain('One.txt');
  1401. expect(selection).toContain('Two.jpg');
  1402. expect(selection).toContain('Three.pdf');
  1403. });
  1404. it('Selects a range when clicking on one file then Shift clicking on another one that is above the first one', function() {
  1405. var $tr = fileList.findFileEl('One.txt');
  1406. var $tr2 = fileList.findFileEl('Three.pdf');
  1407. var e;
  1408. $tr2.find('td.filename input:checkbox').click();
  1409. e = new $.Event('click');
  1410. e.shiftKey = true;
  1411. $tr.find('td.filename .name').trigger(e);
  1412. expect($tr.find('input:checkbox').prop('checked')).toEqual(true);
  1413. expect($tr2.find('input:checkbox').prop('checked')).toEqual(true);
  1414. expect(fileList.findFileEl('Two.jpg').find('input:checkbox').prop('checked')).toEqual(true);
  1415. var selection = _.pluck(fileList.getSelectedFiles(), 'name');
  1416. expect(selection.length).toEqual(3);
  1417. expect(selection).toContain('One.txt');
  1418. expect(selection).toContain('Two.jpg');
  1419. expect(selection).toContain('Three.pdf');
  1420. });
  1421. it('Selecting all files will automatically check "select all" checkbox', function() {
  1422. expect($('.select-all').prop('checked')).toEqual(false);
  1423. $('#fileList tr td.filename input:checkbox').click();
  1424. expect($('.select-all').prop('checked')).toEqual(true);
  1425. });
  1426. it('Selecting all files on the first visible page will not automatically check "select all" checkbox', function() {
  1427. fileList.setFiles(generateFiles(0, 41));
  1428. expect($('.select-all').prop('checked')).toEqual(false);
  1429. $('#fileList tr td.filename input:checkbox').click();
  1430. expect($('.select-all').prop('checked')).toEqual(false);
  1431. });
  1432. it('Clicking "select all" will select/deselect all files', function() {
  1433. fileList.setFiles(generateFiles(0, 41));
  1434. $('.select-all').click();
  1435. expect($('.select-all').prop('checked')).toEqual(true);
  1436. $('#fileList tr input:checkbox').each(function() {
  1437. expect($(this).prop('checked')).toEqual(true);
  1438. });
  1439. expect(_.pluck(fileList.getSelectedFiles(), 'name').length).toEqual(42);
  1440. $('.select-all').click();
  1441. expect($('.select-all').prop('checked')).toEqual(false);
  1442. $('#fileList tr input:checkbox').each(function() {
  1443. expect($(this).prop('checked')).toEqual(false);
  1444. });
  1445. expect(_.pluck(fileList.getSelectedFiles(), 'name').length).toEqual(0);
  1446. });
  1447. it('Clicking "select all" then deselecting a file will uncheck "select all"', function() {
  1448. $('.select-all').click();
  1449. expect($('.select-all').prop('checked')).toEqual(true);
  1450. var $tr = fileList.findFileEl('One.txt');
  1451. $tr.find('input:checkbox').click();
  1452. expect($('.select-all').prop('checked')).toEqual(false);
  1453. expect(_.pluck(fileList.getSelectedFiles(), 'name').length).toEqual(3);
  1454. });
  1455. it('Updates the selection summary when doing a few manipulations with "Select all"', function() {
  1456. $('.select-all').click();
  1457. expect($('.select-all').prop('checked')).toEqual(true);
  1458. var $tr = fileList.findFileEl('One.txt');
  1459. // unselect one
  1460. $tr.find('input:checkbox').click();
  1461. expect($('.select-all').prop('checked')).toEqual(false);
  1462. expect(_.pluck(fileList.getSelectedFiles(), 'name').length).toEqual(3);
  1463. // select all
  1464. $('.select-all').click();
  1465. expect($('.select-all').prop('checked')).toEqual(true);
  1466. expect(_.pluck(fileList.getSelectedFiles(), 'name').length).toEqual(4);
  1467. // unselect one
  1468. $tr.find('input:checkbox').click();
  1469. expect($('.select-all').prop('checked')).toEqual(false);
  1470. expect(_.pluck(fileList.getSelectedFiles(), 'name').length).toEqual(3);
  1471. // re-select it
  1472. $tr.find('input:checkbox').click();
  1473. expect($('.select-all').prop('checked')).toEqual(true);
  1474. expect(_.pluck(fileList.getSelectedFiles(), 'name').length).toEqual(4);
  1475. });
  1476. it('Auto-selects files on next page when "select all" is checked', function() {
  1477. fileList.setFiles(generateFiles(0, 41));
  1478. $('.select-all').click();
  1479. expect(fileList.$fileList.find('tr input:checkbox:checked').length).toEqual(20);
  1480. fileList._nextPage(true);
  1481. expect(fileList.$fileList.find('tr input:checkbox:checked').length).toEqual(40);
  1482. fileList._nextPage(true);
  1483. expect(fileList.$fileList.find('tr input:checkbox:checked').length).toEqual(42);
  1484. expect(_.pluck(fileList.getSelectedFiles(), 'name').length).toEqual(42);
  1485. });
  1486. it('Selecting files updates selection summary', function() {
  1487. var $summary = $('#headerName a.name>span:first');
  1488. expect($summary.text()).toEqual('Name');
  1489. fileList.findFileEl('One.txt').find('input:checkbox').click();
  1490. fileList.findFileEl('Three.pdf').find('input:checkbox').click();
  1491. fileList.findFileEl('somedir').find('input:checkbox').click();
  1492. expect($summary.text()).toEqual('1 folder and 2 files');
  1493. });
  1494. it('Unselecting files hides selection summary', function() {
  1495. var $summary = $('#headerName a.name>span:first');
  1496. fileList.findFileEl('One.txt').find('input:checkbox').click().click();
  1497. expect($summary.text()).toEqual('Name');
  1498. });
  1499. it('Select/deselect files shows/hides file actions', function() {
  1500. var $actions = $('#headerName .selectedActions');
  1501. var $checkbox = fileList.findFileEl('One.txt').find('input:checkbox');
  1502. expect($actions.hasClass('hidden')).toEqual(true);
  1503. $checkbox.click();
  1504. expect($actions.hasClass('hidden')).toEqual(false);
  1505. $checkbox.click();
  1506. expect($actions.hasClass('hidden')).toEqual(true);
  1507. });
  1508. it('Selection is cleared when switching dirs', function() {
  1509. $('.select-all').click();
  1510. var deferredList = $.Deferred();
  1511. var getFolderContentsStub = sinon.stub(filesClient, 'getFolderContents').returns(deferredList.promise());
  1512. fileList.changeDirectory('/');
  1513. deferredList.resolve(200, [testRoot].concat(testFiles));
  1514. expect($('.select-all').prop('checked')).toEqual(false);
  1515. expect(_.pluck(fileList.getSelectedFiles(), 'name')).toEqual([]);
  1516. getFolderContentsStub.restore();
  1517. });
  1518. it('getSelectedFiles returns the selected files even when they are on the next page', function() {
  1519. var selectedFiles;
  1520. fileList.setFiles(generateFiles(0, 41));
  1521. $('.select-all').click();
  1522. // unselect one to not have the "allFiles" case
  1523. fileList.$fileList.find('tr input:checkbox:first').click();
  1524. // only 20 files visible, must still return all the selected ones
  1525. selectedFiles = _.pluck(fileList.getSelectedFiles(), 'name');
  1526. expect(selectedFiles.length).toEqual(41);
  1527. });
  1528. describe('clearing the selection', function() {
  1529. it('clears selected files selected individually calling setFiles()', function() {
  1530. var selectedFiles;
  1531. fileList.setFiles(generateFiles(0, 41));
  1532. fileList.$fileList.find('tr:eq(5) input:checkbox:first').click();
  1533. fileList.$fileList.find('tr:eq(7) input:checkbox:first').click();
  1534. selectedFiles = _.pluck(fileList.getSelectedFiles(), 'name');
  1535. expect(selectedFiles.length).toEqual(2);
  1536. fileList.setFiles(generateFiles(0, 2));
  1537. selectedFiles = _.pluck(fileList.getSelectedFiles(), 'name');
  1538. expect(selectedFiles.length).toEqual(0);
  1539. });
  1540. it('clears selected files selected with select all when calling setFiles()', function() {
  1541. var selectedFiles;
  1542. fileList.setFiles(generateFiles(0, 41));
  1543. $('.select-all').click();
  1544. selectedFiles = _.pluck(fileList.getSelectedFiles(), 'name');
  1545. expect(selectedFiles.length).toEqual(42);
  1546. fileList.setFiles(generateFiles(0, 2));
  1547. selectedFiles = _.pluck(fileList.getSelectedFiles(), 'name');
  1548. expect(selectedFiles.length).toEqual(0);
  1549. });
  1550. });
  1551. describe('Selection overlay', function() {
  1552. it('show doesnt show the delete action if one or more files are not deletable', function () {
  1553. fileList.setFiles(testFiles);
  1554. $('#permissions').val(OC.PERMISSION_READ | OC.PERMISSION_DELETE);
  1555. $('.select-all').click();
  1556. expect(fileList.$el.find('.delete-selected').hasClass('hidden')).toEqual(false);
  1557. testFiles[0].permissions = OC.PERMISSION_READ;
  1558. $('.select-all').click();
  1559. fileList.setFiles(testFiles);
  1560. $('.select-all').click();
  1561. expect(fileList.$el.find('.delete-selected').hasClass('hidden')).toEqual(true);
  1562. });
  1563. });
  1564. describe('Actions', function() {
  1565. beforeEach(function() {
  1566. fileList.findFileEl('One.txt').find('input:checkbox').click();
  1567. fileList.findFileEl('Three.pdf').find('input:checkbox').click();
  1568. fileList.findFileEl('somedir').find('input:checkbox').click();
  1569. });
  1570. it('getSelectedFiles returns the selected file data', function() {
  1571. var files = fileList.getSelectedFiles();
  1572. expect(files.length).toEqual(3);
  1573. expect(files[0]).toEqual({
  1574. id: 1,
  1575. name: 'One.txt',
  1576. mimetype: 'text/plain',
  1577. mtime: 123456789,
  1578. type: 'file',
  1579. size: 12,
  1580. etag: 'abc',
  1581. permissions: OC.PERMISSION_ALL
  1582. });
  1583. expect(files[1]).toEqual({
  1584. id: 3,
  1585. type: 'file',
  1586. name: 'Three.pdf',
  1587. mimetype: 'application/pdf',
  1588. mtime: 234560000,
  1589. size: 58009,
  1590. etag: '123',
  1591. permissions: OC.PERMISSION_ALL
  1592. });
  1593. expect(files[2]).toEqual({
  1594. id: 4,
  1595. type: 'dir',
  1596. name: 'somedir',
  1597. mimetype: 'httpd/unix-directory',
  1598. mtime: 134560000,
  1599. size: 250,
  1600. etag: '456',
  1601. permissions: OC.PERMISSION_ALL
  1602. });
  1603. expect(files[0].id).toEqual(1);
  1604. expect(files[0].name).toEqual('One.txt');
  1605. expect(files[1].id).toEqual(3);
  1606. expect(files[1].name).toEqual('Three.pdf');
  1607. expect(files[2].id).toEqual(4);
  1608. expect(files[2].name).toEqual('somedir');
  1609. });
  1610. it('Removing a file removes it from the selection', function() {
  1611. fileList.remove('Three.pdf');
  1612. var files = fileList.getSelectedFiles();
  1613. expect(files.length).toEqual(2);
  1614. expect(files[0]).toEqual({
  1615. id: 1,
  1616. name: 'One.txt',
  1617. mimetype: 'text/plain',
  1618. mtime: 123456789,
  1619. type: 'file',
  1620. size: 12,
  1621. etag: 'abc',
  1622. permissions: OC.PERMISSION_ALL
  1623. });
  1624. expect(files[1]).toEqual({
  1625. id: 4,
  1626. type: 'dir',
  1627. name: 'somedir',
  1628. mimetype: 'httpd/unix-directory',
  1629. mtime: 134560000,
  1630. size: 250,
  1631. etag: '456',
  1632. permissions: OC.PERMISSION_ALL
  1633. });
  1634. });
  1635. describe('Download', function() {
  1636. it('Opens download URL when clicking "Download"', function() {
  1637. $('.selectedActions .download').click();
  1638. expect(redirectStub.calledOnce).toEqual(true);
  1639. expect(redirectStub.getCall(0).args[0]).toContain(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2Fsubdir&files=%5B%22One.txt%22%2C%22Three.pdf%22%2C%22somedir%22%5D');
  1640. redirectStub.restore();
  1641. });
  1642. it('Downloads root folder when all selected in root folder', function() {
  1643. $('#dir').val('/');
  1644. $('.select-all').click();
  1645. $('.selectedActions .download').click();
  1646. expect(redirectStub.calledOnce).toEqual(true);
  1647. expect(redirectStub.getCall(0).args[0]).toContain(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2F&files=');
  1648. });
  1649. it('Downloads parent folder when all selected in subfolder', function() {
  1650. $('.select-all').click();
  1651. $('.selectedActions .download').click();
  1652. expect(redirectStub.calledOnce).toEqual(true);
  1653. expect(redirectStub.getCall(0).args[0]).toContain(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2F&files=subdir');
  1654. });
  1655. });
  1656. describe('Delete', function() {
  1657. var deleteStub, deferredDelete;
  1658. beforeEach(function() {
  1659. deferredDelete = $.Deferred();
  1660. deleteStub = sinon.stub(filesClient, 'remove').returns(deferredDelete.promise());
  1661. });
  1662. afterEach(function() {
  1663. deleteStub.restore();
  1664. });
  1665. it('Deletes selected files when "Delete" clicked', function() {
  1666. $('.selectedActions .delete-selected').click();
  1667. expect(deleteStub.callCount).toEqual(3);
  1668. expect(deleteStub.getCall(0).args[0]).toEqual('/subdir/One.txt');
  1669. expect(deleteStub.getCall(1).args[0]).toEqual('/subdir/Three.pdf');
  1670. expect(deleteStub.getCall(2).args[0]).toEqual('/subdir/somedir');
  1671. deferredDelete.resolve(204);
  1672. expect(fileList.findFileEl('One.txt').length).toEqual(0);
  1673. expect(fileList.findFileEl('Three.pdf').length).toEqual(0);
  1674. expect(fileList.findFileEl('somedir').length).toEqual(0);
  1675. expect(fileList.findFileEl('Two.jpg').length).toEqual(1);
  1676. });
  1677. it('Deletes all files when all selected when "Delete" clicked', function() {
  1678. $('.select-all').click();
  1679. $('.selectedActions .delete-selected').click();
  1680. expect(deleteStub.callCount).toEqual(4);
  1681. expect(deleteStub.getCall(0).args[0]).toEqual('/subdir/One.txt');
  1682. expect(deleteStub.getCall(1).args[0]).toEqual('/subdir/Two.jpg');
  1683. expect(deleteStub.getCall(2).args[0]).toEqual('/subdir/Three.pdf');
  1684. expect(deleteStub.getCall(3).args[0]).toEqual('/subdir/somedir');
  1685. deferredDelete.resolve(204);
  1686. expect(fileList.isEmpty).toEqual(true);
  1687. });
  1688. });
  1689. });
  1690. it('resets the file selection on reload', function() {
  1691. fileList.$el.find('.select-all').click();
  1692. fileList.reload();
  1693. expect(fileList.$el.find('.select-all').prop('checked')).toEqual(false);
  1694. expect(fileList.getSelectedFiles()).toEqual([]);
  1695. });
  1696. describe('Disabled selection', function() {
  1697. beforeEach(function() {
  1698. fileList._allowSelection = false;
  1699. fileList.setFiles(testFiles);
  1700. });
  1701. it('Does not render checkboxes', function() {
  1702. expect(fileList.$fileList.find('.selectCheckBox').length).toEqual(0);
  1703. });
  1704. it('Does not select a file with Ctrl or Shift if selection is not allowed', function() {
  1705. var $tr = fileList.findFileEl('One.txt');
  1706. var $tr2 = fileList.findFileEl('Three.pdf');
  1707. var e;
  1708. e = new $.Event('click');
  1709. e.ctrlKey = true;
  1710. $tr.find('td.filename .name').trigger(e);
  1711. // click on second entry, does not clear the selection
  1712. e = new $.Event('click');
  1713. e.ctrlKey = true;
  1714. $tr2.find('td.filename .name').trigger(e);
  1715. expect(fileList.getSelectedFiles().length).toEqual(0);
  1716. // deselect now
  1717. e = new $.Event('click');
  1718. e.shiftKey = true;
  1719. $tr2.find('td.filename .name').trigger(e);
  1720. expect(fileList.getSelectedFiles().length).toEqual(0);
  1721. });
  1722. });
  1723. });
  1724. describe('Details sidebar', function() {
  1725. beforeEach(function() {
  1726. fileList.setFiles(testFiles);
  1727. fileList.showDetailsView('Two.jpg');
  1728. });
  1729. describe('registering', function() {
  1730. var addTabStub;
  1731. var addDetailStub;
  1732. beforeEach(function() {
  1733. addTabStub = sinon.stub(OCA.Files.DetailsView.prototype, 'addTabView');
  1734. addDetailStub = sinon.stub(OCA.Files.DetailsView.prototype, 'addDetailView');
  1735. });
  1736. afterEach(function() {
  1737. addTabStub.restore();
  1738. addDetailStub.restore();
  1739. });
  1740. it('forward the registered views to the underlying DetailsView', function() {
  1741. fileList.destroy();
  1742. fileList = new OCA.Files.FileList($('#app-content-files'), {
  1743. detailsViewEnabled: true
  1744. });
  1745. fileList.registerTabView(new OCA.Files.DetailTabView());
  1746. fileList.registerDetailView(new OCA.Files.DetailFileInfoView());
  1747. expect(addTabStub.calledOnce).toEqual(true);
  1748. // twice because the filelist already registers one by default
  1749. expect(addDetailStub.calledTwice).toEqual(true);
  1750. });
  1751. it('does not error when registering panels when not details view configured', function() {
  1752. fileList.destroy();
  1753. fileList = new OCA.Files.FileList($('#app-content-files'), {
  1754. detailsViewEnabled: false
  1755. });
  1756. fileList.registerTabView(new OCA.Files.DetailTabView());
  1757. fileList.registerDetailView(new OCA.Files.DetailFileInfoView());
  1758. expect(addTabStub.notCalled).toEqual(true);
  1759. expect(addDetailStub.notCalled).toEqual(true);
  1760. });
  1761. });
  1762. it('triggers file action when clicking on row if no details view configured', function() {
  1763. fileList.destroy();
  1764. fileList = new OCA.Files.FileList($('#app-content-files'), {
  1765. detailsViewEnabled: false
  1766. });
  1767. var updateDetailsViewStub = sinon.stub(fileList, '_updateDetailsView');
  1768. var actionStub = sinon.stub();
  1769. fileList.setFiles(testFiles);
  1770. fileList.fileActions.register(
  1771. 'text/plain',
  1772. 'Test',
  1773. OC.PERMISSION_ALL,
  1774. function() {
  1775. // Specify icon for hitory button
  1776. return OC.imagePath('core','actions/history');
  1777. },
  1778. actionStub
  1779. );
  1780. fileList.fileActions.setDefault('text/plain', 'Test');
  1781. var $tr = fileList.findFileEl('One.txt');
  1782. $tr.find('td.filename>a.name').click();
  1783. expect(actionStub.calledOnce).toEqual(true);
  1784. expect(updateDetailsViewStub.notCalled).toEqual(true);
  1785. updateDetailsViewStub.restore();
  1786. });
  1787. it('highlights current file when clicked and updates sidebar', function() {
  1788. fileList.fileActions.setDefault('text/plain', 'Test');
  1789. var $tr = fileList.findFileEl('One.txt');
  1790. $tr.find('td.filename>a.name').click();
  1791. expect($tr.hasClass('highlighted')).toEqual(true);
  1792. expect(fileList._detailsView.getFileInfo().id).toEqual(1);
  1793. });
  1794. it('keeps the last highlighted file when clicking outside', function() {
  1795. var $tr = fileList.findFileEl('One.txt');
  1796. $tr.find('td.filename>a.name').click();
  1797. fileList.$el.find('tfoot').click();
  1798. expect($tr.hasClass('highlighted')).toEqual(true);
  1799. expect(fileList._detailsView.getFileInfo().id).toEqual(1);
  1800. });
  1801. it('removes last highlighted file when selecting via checkbox', function() {
  1802. var $tr = fileList.findFileEl('One.txt');
  1803. // select
  1804. $tr.find('td.filename>a.name').click();
  1805. $tr.find('input:checkbox').click();
  1806. expect($tr.hasClass('highlighted')).toEqual(false);
  1807. // deselect
  1808. $tr.find('td.filename>a.name').click();
  1809. $tr.find('input:checkbox').click();
  1810. expect($tr.hasClass('highlighted')).toEqual(false);
  1811. expect(fileList._detailsView.getFileInfo()).toEqual(null);
  1812. });
  1813. it('removes last highlighted file when selecting all files via checkbox', function() {
  1814. var $tr = fileList.findFileEl('One.txt');
  1815. // select
  1816. $tr.find('td.filename>a.name').click();
  1817. fileList.$el.find('.select-all.checkbox').click();
  1818. expect($tr.hasClass('highlighted')).toEqual(false);
  1819. // deselect
  1820. $tr.find('td.filename>a.name').click();
  1821. fileList.$el.find('.select-all.checkbox').click();
  1822. expect($tr.hasClass('highlighted')).toEqual(false);
  1823. expect(fileList._detailsView.getFileInfo()).toEqual(null);
  1824. });
  1825. it('closes sidebar whenever the currently highlighted file was removed from the list', function() {
  1826. var $tr = fileList.findFileEl('One.txt');
  1827. $tr.find('td.filename>a.name').click();
  1828. expect($tr.hasClass('highlighted')).toEqual(true);
  1829. expect(fileList._detailsView.getFileInfo().id).toEqual(1);
  1830. expect($('#app-sidebar').hasClass('disappear')).toEqual(false);
  1831. fileList.remove('One.txt');
  1832. expect($('#app-sidebar').hasClass('disappear')).toEqual(true);
  1833. });
  1834. it('returns the currently selected model instance when calling getModelForFile', function() {
  1835. var $tr = fileList.findFileEl('One.txt');
  1836. $tr.find('td.filename>a.name').click();
  1837. var model1 = fileList.getModelForFile('One.txt');
  1838. var model2 = fileList.getModelForFile('One.txt');
  1839. model1.set('test', true);
  1840. // it's the same model
  1841. expect(model2).toEqual(model1);
  1842. var model3 = fileList.getModelForFile($tr);
  1843. expect(model3).toEqual(model1);
  1844. });
  1845. it('closes the sidebar when switching folders', function() {
  1846. var $tr = fileList.findFileEl('One.txt');
  1847. $tr.find('td.filename>a.name').click();
  1848. expect($('#app-sidebar').hasClass('disappear')).toEqual(false);
  1849. fileList.changeDirectory('/another');
  1850. expect($('#app-sidebar').hasClass('disappear')).toEqual(true);
  1851. });
  1852. });
  1853. describe('File actions', function() {
  1854. it('Clicking on a file name will trigger default action', function() {
  1855. var actionStub = sinon.stub();
  1856. fileList.setFiles(testFiles);
  1857. fileList.fileActions.registerAction({
  1858. mime: 'text/plain',
  1859. name: 'Test',
  1860. type: OCA.Files.FileActions.TYPE_INLINE,
  1861. permissions: OC.PERMISSION_ALL,
  1862. icon: function() {
  1863. // Specify icon for hitory button
  1864. return OC.imagePath('core','actions/history');
  1865. },
  1866. actionHandler: actionStub
  1867. });
  1868. fileList.fileActions.setDefault('text/plain', 'Test');
  1869. var $tr = fileList.findFileEl('One.txt');
  1870. $tr.find('td.filename .nametext').click();
  1871. expect(actionStub.calledOnce).toEqual(true);
  1872. expect(actionStub.getCall(0).args[0]).toEqual('One.txt');
  1873. var context = actionStub.getCall(0).args[1];
  1874. expect(context.$file.is($tr)).toEqual(true);
  1875. expect(context.fileList).toBeDefined();
  1876. expect(context.fileActions).toBeDefined();
  1877. expect(context.dir).toEqual('/subdir');
  1878. });
  1879. it('redisplays actions when new actions have been registered', function() {
  1880. var actionStub = sinon.stub();
  1881. var readyHandler = sinon.stub();
  1882. var clock = sinon.useFakeTimers();
  1883. var debounceStub = sinon.stub(_, 'debounce', function(callback) {
  1884. return function() {
  1885. // defer instead of debounce, to make it work with clock
  1886. _.defer(callback);
  1887. };
  1888. });
  1889. // need to reinit the list to make the debounce call
  1890. fileList.destroy();
  1891. fileList = new OCA.Files.FileList($('#app-content-files'));
  1892. fileList.setFiles(testFiles);
  1893. fileList.$fileList.on('fileActionsReady', readyHandler);
  1894. fileList.fileActions.registerAction({
  1895. mime: 'text/plain',
  1896. name: 'Test',
  1897. type: OCA.Files.FileActions.TYPE_INLINE,
  1898. permissions: OC.PERMISSION_ALL,
  1899. icon: function() {
  1900. // Specify icon for hitory button
  1901. return OC.imagePath('core','actions/history');
  1902. },
  1903. actionHandler: actionStub
  1904. });
  1905. var $tr = fileList.findFileEl('One.txt');
  1906. expect($tr.find('.action-test').length).toEqual(0);
  1907. expect(readyHandler.notCalled).toEqual(true);
  1908. // update is delayed
  1909. clock.tick(100);
  1910. expect($tr.find('.action-test').length).toEqual(1);
  1911. expect(readyHandler.calledOnce).toEqual(true);
  1912. clock.restore();
  1913. debounceStub.restore();
  1914. });
  1915. });
  1916. describe('Sorting files', function() {
  1917. it('Toggles the sort indicator when clicking on a column header', function() {
  1918. var ASC_CLASS = fileList.SORT_INDICATOR_ASC_CLASS;
  1919. var DESC_CLASS = fileList.SORT_INDICATOR_DESC_CLASS;
  1920. fileList.$el.find('.column-size .columntitle').click();
  1921. // moves triangle to size column, check indicator on name is hidden
  1922. expect(
  1923. fileList.$el.find('.column-name .sort-indicator').hasClass('hidden')
  1924. ).toEqual(true);
  1925. // check indicator on size is visible and defaults to descending
  1926. expect(
  1927. fileList.$el.find('.column-size .sort-indicator').hasClass('hidden')
  1928. ).toEqual(false);
  1929. expect(
  1930. fileList.$el.find('.column-size .sort-indicator').hasClass(DESC_CLASS)
  1931. ).toEqual(true);
  1932. // click again on size column, reverses direction
  1933. fileList.$el.find('.column-size .columntitle').click();
  1934. expect(
  1935. fileList.$el.find('.column-size .sort-indicator').hasClass('hidden')
  1936. ).toEqual(false);
  1937. expect(
  1938. fileList.$el.find('.column-size .sort-indicator').hasClass(ASC_CLASS)
  1939. ).toEqual(true);
  1940. // click again on size column, reverses direction
  1941. fileList.$el.find('.column-size .columntitle').click();
  1942. expect(
  1943. fileList.$el.find('.column-size .sort-indicator').hasClass('hidden')
  1944. ).toEqual(false);
  1945. expect(
  1946. fileList.$el.find('.column-size .sort-indicator').hasClass(DESC_CLASS)
  1947. ).toEqual(true);
  1948. // click on mtime column, moves indicator there
  1949. fileList.$el.find('.column-mtime .columntitle').click();
  1950. expect(
  1951. fileList.$el.find('.column-size .sort-indicator').hasClass('hidden')
  1952. ).toEqual(true);
  1953. expect(
  1954. fileList.$el.find('.column-mtime .sort-indicator').hasClass('hidden')
  1955. ).toEqual(false);
  1956. expect(
  1957. fileList.$el.find('.column-mtime .sort-indicator').hasClass(DESC_CLASS)
  1958. ).toEqual(true);
  1959. });
  1960. it('Uses correct sort comparator when inserting files', function() {
  1961. testFiles.sort(OCA.Files.FileList.Comparators.size);
  1962. testFiles.reverse(); //default is descending
  1963. fileList.setFiles(testFiles);
  1964. fileList.$el.find('.column-size .columntitle').click();
  1965. var newFileData = new FileInfo({
  1966. id: 999,
  1967. name: 'new file.txt',
  1968. mimetype: 'text/plain',
  1969. size: 40001,
  1970. etag: '999'
  1971. });
  1972. fileList.add(newFileData);
  1973. expect(fileList.findFileEl('Three.pdf').index()).toEqual(0);
  1974. expect(fileList.findFileEl('new file.txt').index()).toEqual(1);
  1975. expect(fileList.findFileEl('Two.jpg').index()).toEqual(2);
  1976. expect(fileList.findFileEl('somedir').index()).toEqual(3);
  1977. expect(fileList.findFileEl('One.txt').index()).toEqual(4);
  1978. expect(fileList.files.length).toEqual(5);
  1979. expect(fileList.$fileList.find('tr').length).toEqual(5);
  1980. });
  1981. it('Uses correct reversed sort comparator when inserting files', function() {
  1982. testFiles.sort(OCA.Files.FileList.Comparators.size);
  1983. fileList.setFiles(testFiles);
  1984. fileList.$el.find('.column-size .columntitle').click();
  1985. // reverse sort
  1986. fileList.$el.find('.column-size .columntitle').click();
  1987. var newFileData = new FileInfo({
  1988. id: 999,
  1989. name: 'new file.txt',
  1990. mimetype: 'text/plain',
  1991. size: 40001,
  1992. etag: '999'
  1993. });
  1994. fileList.add(newFileData);
  1995. expect(fileList.findFileEl('One.txt').index()).toEqual(0);
  1996. expect(fileList.findFileEl('somedir').index()).toEqual(1);
  1997. expect(fileList.findFileEl('Two.jpg').index()).toEqual(2);
  1998. expect(fileList.findFileEl('new file.txt').index()).toEqual(3);
  1999. expect(fileList.findFileEl('Three.pdf').index()).toEqual(4);
  2000. expect(fileList.files.length).toEqual(5);
  2001. expect(fileList.$fileList.find('tr').length).toEqual(5);
  2002. });
  2003. it('does not sort when clicking on header whenever multiselect is enabled', function() {
  2004. var sortStub = sinon.stub(OCA.Files.FileList.prototype, 'setSort');
  2005. fileList.setFiles(testFiles);
  2006. fileList.findFileEl('One.txt').find('input:checkbox:first').click();
  2007. fileList.$el.find('.column-size .columntitle').click();
  2008. expect(sortStub.notCalled).toEqual(true);
  2009. // can sort again after deselecting
  2010. fileList.findFileEl('One.txt').find('input:checkbox:first').click();
  2011. fileList.$el.find('.column-size .columntitle').click();
  2012. expect(sortStub.calledOnce).toEqual(true);
  2013. sortStub.restore();
  2014. });
  2015. });
  2016. describe('create file', function() {
  2017. var deferredCreate;
  2018. var deferredInfo;
  2019. var createStub;
  2020. var getFileInfoStub;
  2021. beforeEach(function() {
  2022. deferredCreate = $.Deferred();
  2023. deferredInfo = $.Deferred();
  2024. createStub = sinon.stub(filesClient, 'putFileContents')
  2025. .returns(deferredCreate.promise());
  2026. getFileInfoStub = sinon.stub(filesClient, 'getFileInfo')
  2027. .returns(deferredInfo.promise());
  2028. });
  2029. afterEach(function() {
  2030. createStub.restore();
  2031. getFileInfoStub.restore();
  2032. });
  2033. it('creates file with given name and adds it to the list', function() {
  2034. fileList.createFile('test.txt');
  2035. expect(createStub.calledOnce).toEqual(true);
  2036. expect(createStub.getCall(0).args[0]).toEqual('/subdir/test.txt');
  2037. expect(createStub.getCall(0).args[2]).toEqual({
  2038. contentType: 'text/plain',
  2039. overwrite: true
  2040. });
  2041. deferredCreate.resolve(200);
  2042. expect(getFileInfoStub.calledOnce).toEqual(true);
  2043. expect(getFileInfoStub.getCall(0).args[0]).toEqual('/subdir/test.txt');
  2044. deferredInfo.resolve(
  2045. 200,
  2046. new FileInfo({
  2047. path: '/subdir',
  2048. name: 'test.txt',
  2049. mimetype: 'text/plain'
  2050. })
  2051. );
  2052. var $tr = fileList.findFileEl('test.txt');
  2053. expect($tr.length).toEqual(1);
  2054. expect($tr.attr('data-mime')).toEqual('text/plain');
  2055. });
  2056. // TODO: error cases
  2057. // TODO: unique name cases
  2058. });
  2059. describe('create folder', function() {
  2060. var deferredCreate;
  2061. var deferredInfo;
  2062. var createStub;
  2063. var getFileInfoStub;
  2064. beforeEach(function() {
  2065. deferredCreate = $.Deferred();
  2066. deferredInfo = $.Deferred();
  2067. createStub = sinon.stub(filesClient, 'createDirectory')
  2068. .returns(deferredCreate.promise());
  2069. getFileInfoStub = sinon.stub(filesClient, 'getFileInfo')
  2070. .returns(deferredInfo.promise());
  2071. });
  2072. afterEach(function() {
  2073. createStub.restore();
  2074. getFileInfoStub.restore();
  2075. });
  2076. it('creates folder with given name and adds it to the list', function() {
  2077. fileList.createDirectory('sub dir');
  2078. expect(createStub.calledOnce).toEqual(true);
  2079. expect(createStub.getCall(0).args[0]).toEqual('/subdir/sub dir');
  2080. deferredCreate.resolve(200);
  2081. expect(getFileInfoStub.calledOnce).toEqual(true);
  2082. expect(getFileInfoStub.getCall(0).args[0]).toEqual('/subdir/sub dir');
  2083. deferredInfo.resolve(
  2084. 200,
  2085. new FileInfo({
  2086. path: '/subdir',
  2087. name: 'sub dir',
  2088. mimetype: 'httpd/unix-directory'
  2089. })
  2090. );
  2091. var $tr = fileList.findFileEl('sub dir');
  2092. expect($tr.length).toEqual(1);
  2093. expect($tr.attr('data-mime')).toEqual('httpd/unix-directory');
  2094. });
  2095. // TODO: error cases
  2096. // TODO: unique name cases
  2097. });
  2098. /**
  2099. * Test upload mostly by testing the code inside the event handlers
  2100. * that were registered on the magic upload object
  2101. */
  2102. describe('file upload', function() {
  2103. var $uploader;
  2104. beforeEach(function() {
  2105. // note: this isn't the real blueimp file uploader from jquery.fileupload
  2106. // but it makes it possible to simulate the event triggering to
  2107. // test the response of the handlers
  2108. $uploader = $('#file_upload_start');
  2109. fileList.setFiles(testFiles);
  2110. });
  2111. afterEach(function() {
  2112. $uploader = null;
  2113. });
  2114. describe('dropping external files', function() {
  2115. var uploadData;
  2116. /**
  2117. * Simulate drop event on the given target
  2118. *
  2119. * @param $target target element to drop on
  2120. * @return event object including the result
  2121. */
  2122. function dropOn($target, data) {
  2123. var eventData = {
  2124. originalEvent: {
  2125. target: $target
  2126. }
  2127. };
  2128. var ev = new $.Event('fileuploaddrop', eventData);
  2129. // using triggerHandler instead of trigger so we can pass
  2130. // extra data
  2131. $uploader.triggerHandler(ev, data || {});
  2132. return ev;
  2133. }
  2134. beforeEach(function() {
  2135. // simulate data structure from jquery.upload
  2136. uploadData = {
  2137. files: [{
  2138. relativePath: 'fileToUpload.txt'
  2139. }]
  2140. };
  2141. });
  2142. afterEach(function() {
  2143. uploadData = null;
  2144. });
  2145. it('drop on a tr or crumb outside file list does not trigger upload', function() {
  2146. var $anotherTable = $('<table><tbody><tr><td>outside<div class="crumb">crumb</div></td></tr></table>');
  2147. var ev;
  2148. $('#testArea').append($anotherTable);
  2149. ev = dropOn($anotherTable.find('tr'), uploadData);
  2150. expect(ev.result).toEqual(false);
  2151. ev = dropOn($anotherTable.find('.crumb'));
  2152. expect(ev.result).toEqual(false);
  2153. });
  2154. it('drop on an element outside file list container does not trigger upload', function() {
  2155. var $anotherEl = $('<div>outside</div>');
  2156. var ev;
  2157. $('#testArea').append($anotherEl);
  2158. ev = dropOn($anotherEl);
  2159. expect(ev.result).toEqual(false);
  2160. });
  2161. it('drop on an element inside the table triggers upload', function() {
  2162. var ev;
  2163. ev = dropOn(fileList.$fileList.find('th:first'), uploadData);
  2164. expect(ev.result).not.toEqual(false);
  2165. });
  2166. it('drop on an element on the table container triggers upload', function() {
  2167. var ev;
  2168. ev = dropOn($('#app-content-files'), uploadData);
  2169. expect(ev.result).not.toEqual(false);
  2170. });
  2171. it('drop on an element inside the table does not trigger upload if no upload permission', function() {
  2172. $('#permissions').val(0);
  2173. var ev;
  2174. ev = dropOn(fileList.$fileList.find('th:first'));
  2175. expect(ev.result).toEqual(false);
  2176. expect(notificationStub.calledOnce).toEqual(true);
  2177. });
  2178. it('drop on an folder does not trigger upload if no upload permission on that folder', function() {
  2179. var $tr = fileList.findFileEl('somedir');
  2180. var ev;
  2181. $tr.data('permissions', OC.PERMISSION_READ);
  2182. ev = dropOn($tr);
  2183. expect(ev.result).toEqual(false);
  2184. expect(notificationStub.calledOnce).toEqual(true);
  2185. });
  2186. it('drop on a file row inside the table triggers upload to current folder', function() {
  2187. var ev;
  2188. ev = dropOn(fileList.findFileEl('One.txt').find('td:first'), uploadData);
  2189. expect(ev.result).not.toEqual(false);
  2190. });
  2191. it('drop on a folder row inside the table triggers upload to target folder', function() {
  2192. var ev;
  2193. ev = dropOn(fileList.findFileEl('somedir').find('td:eq(2)'), uploadData);
  2194. expect(ev.result).not.toEqual(false);
  2195. expect(uploadData.targetDir).toEqual('/subdir/somedir');
  2196. });
  2197. it('drop on a breadcrumb inside the table triggers upload to target folder', function() {
  2198. var ev;
  2199. fileList.changeDirectory('a/b/c/d');
  2200. ev = dropOn(fileList.$el.find('.crumb:eq(2)'), uploadData);
  2201. expect(ev.result).not.toEqual(false);
  2202. expect(uploadData.targetDir).toEqual('/a/b');
  2203. });
  2204. it('renders upload indicator element for folders only', function() {
  2205. fileList.add({
  2206. name: 'afolder',
  2207. type: 'dir',
  2208. mime: 'httpd/unix-directory'
  2209. });
  2210. fileList.add({
  2211. name: 'afile.txt',
  2212. type: 'file',
  2213. mime: 'text/plain'
  2214. });
  2215. expect(fileList.findFileEl('afolder').find('.uploadtext').length).toEqual(1);
  2216. expect(fileList.findFileEl('afile.txt').find('.uploadtext').length).toEqual(0);
  2217. });
  2218. });
  2219. });
  2220. describe('Handling errors', function () {
  2221. var deferredList;
  2222. var getFolderContentsStub;
  2223. beforeEach(function() {
  2224. deferredList = $.Deferred();
  2225. getFolderContentsStub =
  2226. sinon.stub(filesClient, 'getFolderContents');
  2227. getFolderContentsStub.onCall(0).returns(deferredList.promise());
  2228. getFolderContentsStub.onCall(1).returns($.Deferred().promise());
  2229. fileList.reload();
  2230. });
  2231. afterEach(function() {
  2232. getFolderContentsStub.restore();
  2233. fileList = undefined;
  2234. });
  2235. it('redirects to files app in case of auth error', function () {
  2236. deferredList.reject(401, 'Authentication error');
  2237. expect(redirectStub.calledOnce).toEqual(true);
  2238. expect(redirectStub.getCall(0).args[0]).toEqual(OC.webroot + '/index.php/apps/files');
  2239. expect(getFolderContentsStub.calledOnce).toEqual(true);
  2240. });
  2241. it('redirects to root folder in case of forbidden access', function () {
  2242. deferredList.reject(403);
  2243. expect(fileList.getCurrentDirectory()).toEqual('/');
  2244. expect(getFolderContentsStub.calledTwice).toEqual(true);
  2245. });
  2246. it('redirects to root folder and shows notification in case of internal server error', function () {
  2247. expect(notificationStub.notCalled).toEqual(true);
  2248. deferredList.reject(500);
  2249. expect(fileList.getCurrentDirectory()).toEqual('/');
  2250. expect(getFolderContentsStub.calledTwice).toEqual(true);
  2251. expect(notificationStub.calledOnce).toEqual(true);
  2252. });
  2253. it('redirects to root folder and shows notification in case of storage not available', function () {
  2254. expect(notificationStub.notCalled).toEqual(true);
  2255. deferredList.reject(503, 'Storage not available');
  2256. expect(fileList.getCurrentDirectory()).toEqual('/');
  2257. expect(getFolderContentsStub.calledTwice).toEqual(true);
  2258. expect(notificationStub.calledOnce).toEqual(true);
  2259. });
  2260. });
  2261. describe('showFileBusyState', function() {
  2262. var $tr;
  2263. beforeEach(function() {
  2264. fileList.setFiles(testFiles);
  2265. $tr = fileList.findFileEl('Two.jpg');
  2266. });
  2267. it('shows spinner on busy rows', function() {
  2268. fileList.showFileBusyState('Two.jpg', true);
  2269. expect($tr.hasClass('busy')).toEqual(true);
  2270. expect(OC.TestUtil.getImageUrl($tr.find('.thumbnail')))
  2271. .toEqual(OC.imagePath('core', 'loading.gif'));
  2272. fileList.showFileBusyState('Two.jpg', false);
  2273. expect($tr.hasClass('busy')).toEqual(false);
  2274. expect(OC.TestUtil.getImageUrl($tr.find('.thumbnail')))
  2275. .toEqual(OC.imagePath('core', 'filetypes/image.svg'));
  2276. });
  2277. it('accepts multiple input formats', function() {
  2278. _.each([
  2279. 'Two.jpg',
  2280. ['Two.jpg'],
  2281. $tr,
  2282. [$tr]
  2283. ], function(testCase) {
  2284. fileList.showFileBusyState(testCase, true);
  2285. expect($tr.hasClass('busy')).toEqual(true);
  2286. fileList.showFileBusyState(testCase, false);
  2287. expect($tr.hasClass('busy')).toEqual(false);
  2288. });
  2289. });
  2290. });
  2291. describe('elementToFile', function() {
  2292. var $tr;
  2293. beforeEach(function() {
  2294. fileList.setFiles(testFiles);
  2295. $tr = fileList.findFileEl('One.txt');
  2296. });
  2297. it('converts data attributes to file info structure', function() {
  2298. var fileInfo = fileList.elementToFile($tr);
  2299. expect(fileInfo.id).toEqual(1);
  2300. expect(fileInfo.name).toEqual('One.txt');
  2301. expect(fileInfo.mtime).toEqual(123456789);
  2302. expect(fileInfo.etag).toEqual('abc');
  2303. expect(fileInfo.permissions).toEqual(OC.PERMISSION_ALL);
  2304. expect(fileInfo.size).toEqual(12);
  2305. expect(fileInfo.mimetype).toEqual('text/plain');
  2306. expect(fileInfo.type).toEqual('file');
  2307. expect(fileInfo.path).not.toBeDefined();
  2308. });
  2309. it('adds path attribute if available', function() {
  2310. $tr.attr('data-path', '/subdir');
  2311. var fileInfo = fileList.elementToFile($tr);
  2312. expect(fileInfo.path).toEqual('/subdir');
  2313. });
  2314. });
  2315. describe('new file menu', function() {
  2316. var newFileMenuStub;
  2317. beforeEach(function() {
  2318. newFileMenuStub = sinon.stub(OCA.Files.NewFileMenu.prototype, 'showAt');
  2319. });
  2320. afterEach(function() {
  2321. newFileMenuStub.restore();
  2322. })
  2323. it('renders new button when no legacy upload button exists', function() {
  2324. expect(fileList.$el.find('.button.upload').length).toEqual(0);
  2325. expect(fileList.$el.find('.button.new').length).toEqual(1);
  2326. });
  2327. it('does not render new button when no legacy upload button exists (public page)', function() {
  2328. fileList.destroy();
  2329. $('#controls').append('<input type="button" class="button upload" />');
  2330. fileList = new OCA.Files.FileList($('#app-content-files'));
  2331. expect(fileList.$el.find('.button.upload').length).toEqual(1);
  2332. expect(fileList.$el.find('.button.new').length).toEqual(0);
  2333. });
  2334. it('opens the new file menu when clicking on the "New" button', function() {
  2335. var $button = fileList.$el.find('.button.new');
  2336. $button.click();
  2337. expect(newFileMenuStub.calledOnce).toEqual(true);
  2338. });
  2339. it('does not open the new file menu when button is disabled', function() {
  2340. var $button = fileList.$el.find('.button.new');
  2341. $button.addClass('disabled');
  2342. $button.click();
  2343. expect(newFileMenuStub.notCalled).toEqual(true);
  2344. });
  2345. });
  2346. describe('mount type detection', function() {
  2347. function testMountType(dirInfoId, dirInfoMountType, inputMountType, expectedMountType) {
  2348. var $tr;
  2349. fileList.dirInfo.id = dirInfoId;
  2350. fileList.dirInfo.mountType = dirInfoMountType;
  2351. $tr = fileList.add({
  2352. type: 'dir',
  2353. mimetype: 'httpd/unix-directory',
  2354. name: 'test dir',
  2355. mountType: inputMountType
  2356. });
  2357. expect($tr.attr('data-mounttype')).toEqual(expectedMountType);
  2358. }
  2359. it('leaves mount type as is if no parent exists', function() {
  2360. testMountType(null, null, 'external', 'external');
  2361. testMountType(null, null, 'shared', 'shared');
  2362. });
  2363. it('detects share root if parent exists', function() {
  2364. testMountType(123, null, 'shared', 'shared-root');
  2365. testMountType(123, 'shared', 'shared', 'shared');
  2366. testMountType(123, 'shared-root', 'shared', 'shared');
  2367. });
  2368. it('detects external storage root if parent exists', function() {
  2369. testMountType(123, null, 'external', 'external-root');
  2370. testMountType(123, 'external', 'external', 'external');
  2371. testMountType(123, 'external-root', 'external', 'external');
  2372. });
  2373. });
  2374. });