You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

filelistSpec.js 125KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459
  1. /**
  2. * @copyright 2014 Vincent Petry <pvince81@owncloud.com>
  3. *
  4. * @author Abijeet <abijeetpatro@gmail.com>
  5. * @author Azul <azul@riseup.net>
  6. * @author Bernd Stellwag <burned@zerties.org>
  7. * @author Bjoern Schiessle <bjoern@schiessle.org>
  8. * @author Christoph Wurst <christoph@winzerhof-wurst.at>
  9. * @author Clark Tomlinson <fallen013@gmail.com>
  10. * @author Daniel Calviño Sánchez <danxuliu@gmail.com>
  11. * @author Hasso Tepper <hasso@zone.ee>
  12. * @author Jan-Christoph Borchardt <hey@jancborchardt.net>
  13. * @author Joas Schilling <coding@schilljs.com>
  14. * @author John Molakvoæ <skjnldsv@protonmail.com>
  15. * @author Jörn Friedrich Dreyer <jfd@butonic.de>
  16. * @author Julius Härtl <jus@bitgrid.net>
  17. * @author Lukas Reschke <lukas@statuscode.ch>
  18. * @author Michael Weimann <mail@michael-weimann.eu>
  19. * @author Morris Jobke <hey@morrisjobke.de>
  20. * @author Nazar Mokrynskyi <nazar@mokrynskyi.com>
  21. * @author noveens <noveen.sachdeva@research.iiit.ac.in>
  22. * @author Remco Brenninkmeijer <requist1@starmail.nl>
  23. * @author Robin Appelman <robin@icewind.nl>
  24. * @author Robin McCorkell <robin@mccorkell.me.uk>
  25. * @author Roeland Jago Douma <roeland@famdouma.nl>
  26. * @author Roland Tapken <roland@bitarbeiter.net>
  27. * @author Thomas Citharel <nextcloud@tcit.fr>
  28. * @author Thomas Müller <thomas.mueller@tmit.eu>
  29. * @author Tomasz Grobelny <tomasz@grobelny.net>
  30. * @author Vincent Petry <vincent@nextcloud.com>
  31. *
  32. * @license AGPL-3.0-or-later
  33. *
  34. * This program is free software: you can redistribute it and/or modify
  35. * it under the terms of the GNU Affero General Public License as
  36. * published by the Free Software Foundation, either version 3 of the
  37. * License, or (at your option) any later version.
  38. *
  39. * This program is distributed in the hope that it will be useful,
  40. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  41. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  42. * GNU Affero General Public License for more details.
  43. *
  44. * You should have received a copy of the GNU Affero General Public License
  45. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  46. *
  47. */
  48. describe('OCA.Files.FileList tests', function() {
  49. var FileInfo = OC.Files.FileInfo;
  50. var testFiles, testRoot, notificationStub, fileList, pageSizeStub;
  51. var bcResizeStub;
  52. var filesClient;
  53. var filesConfig;
  54. var redirectStub;
  55. /**
  56. * Generate test file data
  57. */
  58. function generateFiles(startIndex, endIndex) {
  59. var files = [];
  60. var name;
  61. for (var i = startIndex; i <= endIndex; i++) {
  62. name = 'File with index ';
  63. if (i < 10) {
  64. // do not rely on localeCompare here
  65. // and make the sorting predictable
  66. // cross-browser
  67. name += '0';
  68. }
  69. name += i + '.txt';
  70. files.push(new FileInfo({
  71. id: i,
  72. type: 'file',
  73. name: name,
  74. mimetype: 'text/plain',
  75. size: i * 2,
  76. etag: 'abc'
  77. }));
  78. }
  79. return files;
  80. }
  81. beforeEach(function() {
  82. filesConfig = new OC.Backbone.Model({
  83. showhidden: true
  84. });
  85. filesClient = new OC.Files.Client({
  86. host: 'localhost',
  87. port: 80,
  88. // FIXME: uncomment after fixing the test OC.getRootPath()
  89. //root: OC.getRootPath() + '/remote.php/webdav',
  90. root: '/remote.php/webdav',
  91. useHTTPS: false
  92. });
  93. redirectStub = sinon.stub(OC, 'redirect');
  94. notificationStub = sinon.stub(OC.Notification, 'show');
  95. // prevent resize algo to mess up breadcrumb order while
  96. // testing
  97. bcResizeStub = sinon.stub(OCA.Files.BreadCrumb.prototype, '_resize');
  98. // init parameters and test table elements
  99. $('#testArea').append(
  100. '<div id="app-content-files">' +
  101. // init horrible parameters
  102. '<input type="hidden" id="dir" value="/subdir"/>' +
  103. '<input type="hidden" id="permissions" value="31"/>' +
  104. // dummy controls
  105. '<div id="controls">' +
  106. ' <div class="actions creatable"></div>' +
  107. ' <div class="notCreatable"></div>' +
  108. '</div>' +
  109. // uploader
  110. '<input type="file" id="file_upload_start" name="files[]" multiple="multiple">' +
  111. // dummy table
  112. // TODO: at some point this will be rendered by the fileList class itself!
  113. '<table id="filestable" class="list-container view-grid">' +
  114. '<thead><tr>' +
  115. '<th id="headerName" class="hidden column-name">' +
  116. '<input type="checkbox" id="select_all_files" class="select-all checkbox">' +
  117. '<a class="name columntitle" href="#" onclick="event.preventDefault()" data-sort="name"><span>Name</span><span class="sort-indicator"></span></a>' +
  118. '<span id="selectedActionsList" class="selectedActions hidden">' +
  119. '<a class="actions-selected" href="#" onclick="event.preventDefault()"><span class="icon icon-more"></span><span>Actions</span></a>' +
  120. '</th>' +
  121. '<th class="hidden column-size"><a class="columntitle" href="#" onclick="event.preventDefault()" data-sort="size"><span class="sort-indicator"></span></a></th>' +
  122. '<th class="hidden column-mtime"><a class="columntitle" href="#" onclick="event.preventDefault()" data-sort="mtime"><span class="sort-indicator"></span></a></th>' +
  123. '</tr></thead>' +
  124. '<tbody id="fileList"></tbody>' +
  125. '<tfoot></tfoot>' +
  126. '</table>' +
  127. // TODO: move to handlebars template
  128. '<div id="emptycontent"><h2>Empty content message</h2><p class="uploadmessage">Upload message</p></div>' +
  129. '<div class="nofilterresults hidden"></div>' +
  130. '</div>'
  131. );
  132. testRoot = new FileInfo({
  133. // root entry
  134. id: 99,
  135. type: 'dir',
  136. name: '/subdir',
  137. mimetype: 'httpd/unix-directory',
  138. size: 1200000,
  139. etag: 'a0b0c0d0',
  140. permissions: OC.PERMISSION_ALL
  141. });
  142. testFiles = [new FileInfo({
  143. id: 1,
  144. type: 'file',
  145. name: 'One.txt',
  146. mimetype: 'text/plain',
  147. mtime: 123456789,
  148. size: 12,
  149. etag: 'abc',
  150. permissions: OC.PERMISSION_ALL
  151. }), new FileInfo({
  152. id: 2,
  153. type: 'file',
  154. name: 'Two.jpg',
  155. mimetype: 'image/jpeg',
  156. mtime: 234567890,
  157. size: 12049,
  158. etag: 'def',
  159. permissions: OC.PERMISSION_ALL
  160. }), new FileInfo({
  161. id: 3,
  162. type: 'file',
  163. name: 'Three.pdf',
  164. mimetype: 'application/pdf',
  165. mtime: 234560000,
  166. size: 58009,
  167. etag: '123',
  168. permissions: OC.PERMISSION_ALL
  169. }), new FileInfo({
  170. id: 4,
  171. type: 'dir',
  172. name: 'somedir',
  173. mimetype: 'httpd/unix-directory',
  174. mtime: 134560000,
  175. size: 250,
  176. etag: '456',
  177. permissions: OC.PERMISSION_ALL
  178. })];
  179. pageSizeStub = sinon.stub(OCA.Files.FileList.prototype, 'pageSize').returns(20);
  180. fileList = new OCA.Files.FileList($('#app-content-files'), {
  181. filesClient: filesClient,
  182. config: filesConfig,
  183. enableUpload: true,
  184. multiSelectMenu: [{
  185. name: 'copyMove',
  186. displayName: t('files', 'Move or copy'),
  187. iconClass: 'icon-external',
  188. },
  189. {
  190. name: 'download',
  191. displayName: t('files', 'Download'),
  192. iconClass: 'icon-download',
  193. },
  194. {
  195. name: 'delete',
  196. displayName: t('files', 'Delete'),
  197. iconClass: 'icon-delete',
  198. }]
  199. });
  200. });
  201. afterEach(function() {
  202. testFiles = undefined;
  203. if (fileList) {
  204. fileList.destroy();
  205. }
  206. fileList = undefined;
  207. notificationStub.restore();
  208. bcResizeStub.restore();
  209. pageSizeStub.restore();
  210. redirectStub.restore();
  211. });
  212. describe('Getters', function() {
  213. it('Returns the current directory', function() {
  214. $('#dir').val('/one/two/three');
  215. expect(fileList.getCurrentDirectory()).toEqual('/one/two/three');
  216. });
  217. it('Returns the directory permissions as int', function() {
  218. $('#permissions').val('23');
  219. expect(fileList.getDirectoryPermissions()).toEqual(23);
  220. });
  221. });
  222. describe('Adding files', function() {
  223. it('generates file element with correct attributes when calling add() with file data', function() {
  224. var fileData = new FileInfo({
  225. id: 18,
  226. name: 'testName.txt',
  227. mimetype: 'text/plain',
  228. size: 1234,
  229. etag: 'a01234c',
  230. mtime: 123456
  231. });
  232. var $tr = fileList.add(fileData);
  233. expect($tr).toBeDefined();
  234. expect($tr[0].tagName.toLowerCase()).toEqual('tr');
  235. expect($tr.attr('data-id')).toEqual('18');
  236. expect($tr.attr('data-type')).toEqual('file');
  237. expect($tr.attr('data-file')).toEqual('testName.txt');
  238. expect($tr.attr('data-size')).toEqual('1234');
  239. expect($tr.attr('data-etag')).toEqual('a01234c');
  240. expect($tr.attr('data-permissions')).toEqual('31');
  241. expect($tr.attr('data-mime')).toEqual('text/plain');
  242. expect($tr.attr('data-mtime')).toEqual('123456');
  243. expect($tr.attr('data-e2eencrypted')).toEqual('false');
  244. expect($tr.find('a.name').attr('href'))
  245. .toEqual(OC.getRootPath() + '/remote.php/webdav/subdir/testName.txt');
  246. expect($tr.find('.nametext').text().trim()).toEqual('testName.txt');
  247. expect($tr.find('.filesize').text()).toEqual('1 KB');
  248. expect($tr.find('.date').text()).not.toEqual('?');
  249. expect(fileList.findFileEl('testName.txt')[0]).toEqual($tr[0]);
  250. });
  251. it('generates file element with url for default action when one is defined', function() {
  252. var actionStub = sinon.stub();
  253. fileList.setFiles(testFiles);
  254. fileList.fileActions.registerAction({
  255. mime: 'text/plain',
  256. name: 'Test',
  257. type: OCA.Files.FileActions.TYPE_INLINE,
  258. permissions: OC.PERMISSION_ALL,
  259. icon: function() {
  260. // Specify icon for hitory button
  261. return OC.imagePath('core','actions/history');
  262. },
  263. actionHandler: actionStub
  264. });
  265. fileList.fileActions.setDefault('text/plain', 'Test');
  266. var fileData = new FileInfo({
  267. id: 18,
  268. name: 'testName.txt',
  269. mimetype: 'text/plain',
  270. size: 1234,
  271. etag: 'a01234c',
  272. mtime: 123456
  273. });
  274. var $tr = fileList.add(fileData);
  275. expect($tr.find('a.name').attr('href'))
  276. .toEqual(OC.getRootPath() + '/index.php/apps/files?dir=&openfile=18');
  277. });
  278. it('generates dir element with correct attributes when calling add() with dir data', function() {
  279. var fileData = new FileInfo({
  280. id: 19,
  281. name: 'testFolder',
  282. mimetype: 'httpd/unix-directory',
  283. size: 1234,
  284. etag: 'a01234c',
  285. mtime: 123456
  286. });
  287. var $tr = fileList.add(fileData);
  288. expect($tr).toBeDefined();
  289. expect($tr[0].tagName.toLowerCase()).toEqual('tr');
  290. expect($tr.attr('data-id')).toEqual('19');
  291. expect($tr.attr('data-type')).toEqual('dir');
  292. expect($tr.attr('data-file')).toEqual('testFolder');
  293. expect($tr.attr('data-size')).toEqual('1234');
  294. expect($tr.attr('data-etag')).toEqual('a01234c');
  295. expect($tr.attr('data-permissions')).toEqual('31');
  296. expect($tr.attr('data-mime')).toEqual('httpd/unix-directory');
  297. expect($tr.attr('data-mtime')).toEqual('123456');
  298. expect($tr.attr('data-e2eencrypted')).toEqual('false');
  299. expect($tr.find('.filesize').text()).toEqual('1 KB');
  300. expect($tr.find('.date').text()).not.toEqual('?');
  301. expect(fileList.findFileEl('testFolder')[0]).toEqual($tr[0]);
  302. });
  303. it('generates file element with default attributes when calling add() with minimal data', function() {
  304. var fileData = {
  305. type: 'file',
  306. name: 'testFile.txt'
  307. };
  308. var $tr = fileList.add(fileData);
  309. expect($tr).toBeDefined();
  310. expect($tr[0].tagName.toLowerCase()).toEqual('tr');
  311. expect($tr.attr('data-id')).toBeUndefined();
  312. expect($tr.attr('data-type')).toEqual('file');
  313. expect($tr.attr('data-file')).toEqual('testFile.txt');
  314. expect($tr.attr('data-size')).toBeUndefined();
  315. expect($tr.attr('data-etag')).toBeUndefined();
  316. expect($tr.attr('data-permissions')).toEqual('31');
  317. expect($tr.attr('data-mime')).toBeUndefined();
  318. expect($tr.attr('data-e2eencrypted')).toEqual('false');
  319. expect($tr.find('.filesize').text()).toEqual('Pending');
  320. expect($tr.find('.date').text()).not.toEqual('?');
  321. });
  322. it('generates dir element with default attributes when calling add() with minimal data', function() {
  323. var fileData = {
  324. type: 'dir',
  325. name: 'testFolder'
  326. };
  327. var $tr = fileList.add(fileData);
  328. expect($tr).toBeDefined();
  329. expect($tr[0].tagName.toLowerCase()).toEqual('tr');
  330. expect($tr.attr('data-id')).toBeUndefined();
  331. expect($tr.attr('data-type')).toEqual('dir');
  332. expect($tr.attr('data-file')).toEqual('testFolder');
  333. expect($tr.attr('data-size')).toBeUndefined();
  334. expect($tr.attr('data-etag')).toBeUndefined();
  335. expect($tr.attr('data-permissions')).toEqual('31');
  336. expect($tr.attr('data-mime')).toEqual('httpd/unix-directory');
  337. expect($tr.attr('data-e2eencrypted')).toEqual('false');
  338. expect($tr.find('.filesize').text()).toEqual('Pending');
  339. expect($tr.find('.date').text()).not.toEqual('?');
  340. });
  341. it('generates dir element with true e2eencrypted attribute when calling add() with minimal data including isEncrypted', function() {
  342. var fileData = {
  343. type: 'dir',
  344. name: 'testFolder',
  345. isEncrypted: true
  346. };
  347. var $tr = fileList.add(fileData);
  348. expect($tr.attr('data-e2eencrypted')).toEqual('true');
  349. });
  350. it('generates file element with no permissions when permissions are explicitly none', function() {
  351. var fileData = {
  352. type: 'dir',
  353. name: 'testFolder',
  354. permissions: OC.PERMISSION_NONE
  355. };
  356. var $tr = fileList.add(fileData);
  357. expect($tr.attr('data-permissions')).toEqual('0');
  358. });
  359. it('generates file element with zero size when size is explicitly zero', function() {
  360. var fileData = {
  361. type: 'dir',
  362. name: 'testFolder',
  363. size: '0'
  364. };
  365. var $tr = fileList.add(fileData);
  366. expect($tr.find('.filesize').text()).toEqual('0 KB');
  367. });
  368. it('generates file element with unknown date when mtime invalid', function() {
  369. var fileData = {
  370. type: 'dir',
  371. name: 'testFolder',
  372. mtime: -1
  373. };
  374. var $tr = fileList.add(fileData);
  375. expect($tr.find('.date .modified').text()).toEqual('?');
  376. });
  377. it('adds new file to the end of the list', function() {
  378. var $tr;
  379. var fileData = {
  380. type: 'file',
  381. name: 'ZZZ.txt'
  382. };
  383. fileList.setFiles(testFiles);
  384. $tr = fileList.add(fileData);
  385. expect($tr.index()).toEqual(4);
  386. });
  387. it('inserts files in a sorted manner when insert option is enabled', function() {
  388. for (var i = 0; i < testFiles.length; i++) {
  389. fileList.add(testFiles[i]);
  390. }
  391. expect(fileList.files[0].name).toEqual('somedir');
  392. expect(fileList.files[1].name).toEqual('One.txt');
  393. expect(fileList.files[2].name).toEqual('Three.pdf');
  394. expect(fileList.files[3].name).toEqual('Two.jpg');
  395. });
  396. it('inserts new file at correct position', function() {
  397. var $tr;
  398. var fileData = {
  399. type: 'file',
  400. name: 'P comes after O.txt'
  401. };
  402. for (var i = 0; i < testFiles.length; i++) {
  403. fileList.add(testFiles[i]);
  404. }
  405. $tr = fileList.add(fileData);
  406. // after "One.txt"
  407. expect($tr.index()).toEqual(2);
  408. expect(fileList.files[2]).toEqual(fileData);
  409. });
  410. it('inserts new folder at correct position in insert mode', function() {
  411. var $tr;
  412. var fileData = {
  413. type: 'dir',
  414. name: 'somedir2 comes after somedir'
  415. };
  416. for (var i = 0; i < testFiles.length; i++) {
  417. fileList.add(testFiles[i]);
  418. }
  419. $tr = fileList.add(fileData);
  420. expect($tr.index()).toEqual(1);
  421. expect(fileList.files[1]).toEqual(fileData);
  422. });
  423. it('inserts new file at the end correctly', function() {
  424. var $tr;
  425. var fileData = {
  426. type: 'file',
  427. name: 'zzz.txt'
  428. };
  429. for (var i = 0; i < testFiles.length; i++) {
  430. fileList.add(testFiles[i]);
  431. }
  432. $tr = fileList.add(fileData);
  433. expect($tr.index()).toEqual(4);
  434. expect(fileList.files[4]).toEqual(fileData);
  435. });
  436. it('removes empty content message and shows summary when adding first file', function() {
  437. var $summary;
  438. var fileData = {
  439. type: 'file',
  440. name: 'first file.txt',
  441. size: 12
  442. };
  443. fileList.setFiles([]);
  444. expect(fileList.isEmpty).toEqual(true);
  445. fileList.add(fileData);
  446. $summary = $('#filestable .summary');
  447. expect($summary.hasClass('hidden')).toEqual(false);
  448. // yes, ugly...
  449. expect($summary.find('.fileinfo').text()).toEqual('1 file');
  450. expect($summary.find('.dirinfo').hasClass('hidden')).toEqual(true);
  451. expect($summary.find('.connector').hasClass('hidden')).toEqual(true);
  452. expect($summary.find('.fileinfo').hasClass('hidden')).toEqual(false);
  453. expect($summary.find('.filesize').text()).toEqual('12 B');
  454. expect($('#filestable thead th').hasClass('hidden')).toEqual(false);
  455. expect($('#emptycontent').hasClass('hidden')).toEqual(true);
  456. expect(fileList.isEmpty).toEqual(false);
  457. });
  458. it('correctly adds the extension markup and show hidden files completely in gray', function() {
  459. var $tr;
  460. var testDataAndExpectedResult = [
  461. {file: {type: 'file', name: 'ZZZ.txt'}, extension: '.txt'},
  462. {file: {type: 'file', name: 'ZZZ.tar.gz'}, extension: '.gz'},
  463. {file: {type: 'file', name: 'test.with.some.dots.in.it.txt'}, extension: '.txt'},
  464. // we render hidden files completely in gray
  465. {file: {type: 'file', name: '.test.with.some.dots.in.it.txt'}, extension: '.test.with.some.dots.in.it.txt'},
  466. {file: {type: 'file', name: '.hidden'}, extension: '.hidden'},
  467. ];
  468. fileList.setFiles(testFiles);
  469. for(var i = 0; i < testDataAndExpectedResult.length; i++) {
  470. var testSet = testDataAndExpectedResult[i];
  471. var fileData = testSet['file'];
  472. $tr = fileList.add(fileData);
  473. expect($tr.find('.nametext .extension').text()).toEqual(testSet['extension']);
  474. }
  475. });
  476. });
  477. describe('Hidden files', function() {
  478. it('sets the class hidden-file for hidden files', function() {
  479. var fileData = {
  480. type: 'dir',
  481. name: '.testFolder'
  482. };
  483. var $tr = fileList.add(fileData);
  484. expect($tr).toBeDefined();
  485. expect($tr.hasClass('hidden-file')).toEqual(true);
  486. });
  487. it('does not set the class hidden-file for visible files', function() {
  488. var fileData = {
  489. type: 'dir',
  490. name: 'testFolder'
  491. };
  492. var $tr = fileList.add(fileData);
  493. expect($tr).toBeDefined();
  494. expect($tr.hasClass('hidden-file')).toEqual(false);
  495. });
  496. it('toggles the list\'s class when toggling hidden files', function() {
  497. expect(fileList.$el.hasClass('hide-hidden-files')).toEqual(false);
  498. filesConfig.set('showhidden', false);
  499. expect(fileList.$el.hasClass('hide-hidden-files')).toEqual(true);
  500. filesConfig.set('showhidden', true);
  501. expect(fileList.$el.hasClass('hide-hidden-files')).toEqual(false);
  502. });
  503. });
  504. describe('Removing files from the list', function() {
  505. it('Removes file from list when calling remove() and updates summary', function() {
  506. var $summary;
  507. var $removedEl;
  508. fileList.setFiles(testFiles);
  509. $removedEl = fileList.remove('One.txt');
  510. expect($removedEl).toBeDefined();
  511. expect($removedEl.attr('data-file')).toEqual('One.txt');
  512. expect($('#fileList tr').length).toEqual(3);
  513. expect(fileList.files.length).toEqual(3);
  514. expect(fileList.findFileEl('One.txt').length).toEqual(0);
  515. $summary = $('#filestable .summary');
  516. expect($summary.hasClass('hidden')).toEqual(false);
  517. expect($summary.find('.dirinfo').text()).toEqual('1 folder');
  518. expect($summary.find('.fileinfo').text()).toEqual('2 files');
  519. expect($summary.find('.dirinfo').hasClass('hidden')).toEqual(false);
  520. expect($summary.find('.fileinfo').hasClass('hidden')).toEqual(false);
  521. expect($summary.find('.filesize').text()).toEqual('69 KB');
  522. expect(fileList.isEmpty).toEqual(false);
  523. });
  524. it('Shows empty content when removing last file', function() {
  525. var $summary;
  526. fileList.setFiles([testFiles[0]]);
  527. fileList.remove('One.txt');
  528. expect($('#fileList tr').length).toEqual(0);
  529. expect(fileList.files.length).toEqual(0);
  530. expect(fileList.findFileEl('One.txt').length).toEqual(0);
  531. $summary = $('#filestable .summary');
  532. expect($summary.hasClass('hidden')).toEqual(true);
  533. expect($('#filestable thead th').hasClass('hidden')).toEqual(true);
  534. expect($('#emptycontent').hasClass('hidden')).toEqual(false);
  535. expect(fileList.isEmpty).toEqual(true);
  536. });
  537. });
  538. describe('Deleting files', function() {
  539. var deferredDelete;
  540. var deleteStub;
  541. beforeEach(function() {
  542. deferredDelete = $.Deferred();
  543. deleteStub = sinon.stub(filesClient, 'remove');
  544. });
  545. afterEach(function() {
  546. deleteStub.restore();
  547. });
  548. function doDelete() {
  549. // note: normally called from FileActions
  550. return fileList.do_delete(['One.txt', 'Two.jpg']).then(function(){
  551. expect(deleteStub.calledTwice).toEqual(true);
  552. expect(deleteStub.getCall(0).args[0]).toEqual('/subdir/One.txt');
  553. expect(deleteStub.getCall(1).args[0]).toEqual('/subdir/Two.jpg');
  554. });
  555. }
  556. it('calls delete.php, removes the deleted entries and updates summary', function(done) {
  557. var $summary;
  558. fileList.setFiles(testFiles);
  559. deferredDelete1 = $.Deferred();
  560. deferredDelete2 = $.Deferred();
  561. deleteStub.onCall(0).callsFake(function(src){
  562. expect(deleteStub.calledOnce).toEqual(true);
  563. expect(src).toEqual('/subdir/One.txt');
  564. return deferredDelete1.promise();
  565. });
  566. deleteStub.onCall(1).callsFake(function(src){
  567. expect(deleteStub.calledTwice).toEqual(true);
  568. expect(src).toEqual('/subdir/Two.jpg');
  569. return deferredDelete2.promise();
  570. });
  571. var promise = fileList.do_delete(['One.txt', 'Two.jpg']);
  572. deferredDelete1.resolve(200);
  573. deferredDelete2.resolve(200);
  574. return promise.then(function(){
  575. expect(fileList.findFileEl('One.txt').length).toEqual(0);
  576. expect(fileList.findFileEl('Two.jpg').length).toEqual(0);
  577. expect(fileList.findFileEl('Three.pdf').length).toEqual(1);
  578. expect(fileList.$fileList.find('tr').length).toEqual(2);
  579. $summary = $('#filestable .summary');
  580. expect($summary.hasClass('hidden')).toEqual(false);
  581. expect($summary.find('.dirinfo').text()).toEqual('1 folder');
  582. expect($summary.find('.fileinfo').text()).toEqual('1 file');
  583. expect($summary.find('.dirinfo').hasClass('hidden')).toEqual(false);
  584. expect($summary.find('.fileinfo').hasClass('hidden')).toEqual(false);
  585. expect($summary.find('.filesize').text()).toEqual('57 KB');
  586. expect(fileList.isEmpty).toEqual(false);
  587. expect($('#filestable thead th').hasClass('hidden')).toEqual(false);
  588. expect($('#emptycontent').hasClass('hidden')).toEqual(true);
  589. expect(notificationStub.notCalled).toEqual(true);
  590. }).then(done, done);
  591. });
  592. it('shows busy state on files to be deleted', function(done) {
  593. fileList.setFiles(testFiles);
  594. deferredDelete1 = $.Deferred();
  595. deferredDelete2 = $.Deferred();
  596. deleteStub.onCall(0).callsFake(function(src){
  597. expect(fileList.findFileEl('One.txt').hasClass('busy')).toEqual(true);
  598. expect(fileList.findFileEl('Three.pdf').hasClass('busy')).toEqual(false);
  599. expect(deleteStub.calledOnce).toEqual(true);
  600. expect(src).toEqual('/subdir/One.txt');
  601. return deferredDelete1.promise();
  602. });
  603. deleteStub.onCall(1).callsFake(function(src){
  604. expect(fileList.findFileEl('Two.jpg').hasClass('busy')).toEqual(true);
  605. expect(fileList.findFileEl('Three.pdf').hasClass('busy')).toEqual(false);
  606. expect(deleteStub.calledTwice).toEqual(true);
  607. expect(src).toEqual('/subdir/Two.jpg');
  608. return deferredDelete2.promise();
  609. });
  610. var promise = fileList.do_delete(['One.txt', 'Two.jpg']).then(function(){
  611. expect(deleteStub.calledTwice).toEqual(true);
  612. });
  613. deferredDelete1.resolve(200);
  614. deferredDelete2.resolve(200);
  615. return promise.then(function(){
  616. expect(fileList.findFileEl('One.txt').hasClass('busy')).toEqual(false);
  617. expect(fileList.findFileEl('Two.jpg').hasClass('busy')).toEqual(false);
  618. }).then(done, done);
  619. });
  620. it('shows busy state on all files when deleting all', function(done) {
  621. fileList.setFiles(testFiles);
  622. var deferredDeleteArray = [];
  623. var count = 0;
  624. for (var i = 0; i < 4; i++) {
  625. (function(i, fn){
  626. deferredDeleteArray.push($.Deferred());
  627. deleteStub.onCall(i).callsFake(function(src){
  628. expect(fileList.findFileEl(fn).hasClass('busy')).toEqual(true);
  629. count++;
  630. return deferredDeleteArray[i].promise();
  631. });
  632. })(i, testFiles[i].name);
  633. }
  634. var promise = fileList.do_delete();
  635. for (var i = 0; i < 4; i++) {
  636. deferredDeleteArray[i].resolve(200);
  637. }
  638. return promise.then(function(){
  639. expect(count).toEqual(4);
  640. }).then(done, done);
  641. });
  642. it('updates summary when deleting last file', function(done) {
  643. var $summary;
  644. fileList.setFiles([testFiles[0], testFiles[1]]);
  645. deleteStub.returns(deferredDelete.promise());
  646. deferredDelete.resolve(200);
  647. return doDelete().then(function(){
  648. expect(fileList.$fileList.find('tr').length).toEqual(0);
  649. $summary = $('#filestable .summary');
  650. expect($summary.hasClass('hidden')).toEqual(true);
  651. expect(fileList.isEmpty).toEqual(true);
  652. expect(fileList.files.length).toEqual(0);
  653. expect($('#filestable thead th').hasClass('hidden')).toEqual(true);
  654. expect($('#emptycontent').hasClass('hidden')).toEqual(false);
  655. }).then(done, done);
  656. });
  657. it('bring back deleted item when delete call failed', function(done) {
  658. fileList.setFiles(testFiles);
  659. deleteStub.returns(deferredDelete.promise());
  660. var promise = doDelete();
  661. deferredDelete.reject(403);
  662. return promise.then(function(){
  663. // files are still in the list
  664. expect(fileList.findFileEl('One.txt').length).toEqual(1);
  665. expect(fileList.findFileEl('Two.jpg').length).toEqual(1);
  666. expect(fileList.$fileList.find('tr').length).toEqual(4);
  667. expect(notificationStub.calledTwice).toEqual(true);
  668. }).then(done, done);
  669. });
  670. it('remove file from list if delete call returned 404 not found', function(done) {
  671. fileList.setFiles(testFiles);
  672. deleteStub.returns(deferredDelete.promise());
  673. var promise = doDelete();
  674. deferredDelete.reject(404);
  675. return promise.then(function(){
  676. expect(fileList.findFileEl('One.txt').length).toEqual(0);
  677. expect(fileList.findFileEl('Two.jpg').length).toEqual(0);
  678. expect(fileList.$fileList.find('tr').length).toEqual(2);
  679. expect(notificationStub.notCalled).toEqual(true);
  680. }).then(done, done);
  681. });
  682. });
  683. describe('Renaming files', function() {
  684. var deferredRename;
  685. var renameStub;
  686. beforeEach(function() {
  687. deferredRename = $.Deferred();
  688. renameStub = sinon.stub(filesClient, 'move').returns(deferredRename.promise());
  689. for (var i = 0; i < testFiles.length; i++) {
  690. var file = testFiles[i];
  691. file.path = '/some/subdir';
  692. fileList.add(file, {silent: true});
  693. }
  694. });
  695. afterEach(function() {
  696. renameStub.restore();
  697. });
  698. function doCancelRename() {
  699. var $input;
  700. // trigger rename prompt
  701. fileList.rename('One.txt');
  702. $input = fileList.$fileList.find('input.filename');
  703. // keep same name
  704. $input.val('One.txt');
  705. // trigger submit because triggering blur doesn't work in all browsers
  706. $input.closest('form').trigger('submit');
  707. expect(renameStub.notCalled).toEqual(true);
  708. }
  709. function doRename() {
  710. var $input;
  711. // trigger rename prompt
  712. fileList.rename('One.txt');
  713. $input = fileList.$fileList.find('input.filename');
  714. $input.val('Tu_after_three.txt');
  715. // trigger submit because triggering blur doesn't work in all browsers
  716. $input.closest('form').trigger('submit');
  717. expect(renameStub.calledOnce).toEqual(true);
  718. expect(renameStub.getCall(0).args[0]).toEqual('/some/subdir/One.txt');
  719. expect(renameStub.getCall(0).args[1]).toEqual('/some/subdir/Tu_after_three.txt');
  720. }
  721. it('Inserts renamed file entry at correct position if rename ajax call suceeded', function() {
  722. doRename();
  723. deferredRename.resolve(201);
  724. // element stays renamed
  725. expect(fileList.findFileEl('One.txt').length).toEqual(0);
  726. expect(fileList.findFileEl('Tu_after_three.txt').length).toEqual(1);
  727. expect(fileList.findFileEl('Tu_after_three.txt').index()).toEqual(2); // after Two.jpg
  728. expect(notificationStub.notCalled).toEqual(true);
  729. });
  730. it('Reverts file entry if rename ajax call failed', function() {
  731. doRename();
  732. deferredRename.reject(403);
  733. // element was reverted
  734. expect(fileList.findFileEl('One.txt').length).toEqual(1);
  735. expect(fileList.findFileEl('One.txt').index()).toEqual(1); // after somedir
  736. expect(fileList.findFileEl('Tu_after_three.txt').length).toEqual(0);
  737. expect(notificationStub.calledOnce).toEqual(true);
  738. });
  739. it('Correctly updates file link after rename', function() {
  740. var $tr;
  741. doRename();
  742. deferredRename.resolve(201);
  743. $tr = fileList.findFileEl('Tu_after_three.txt');
  744. expect($tr.find('a.name').attr('href'))
  745. .toEqual(OC.getRootPath() + '/remote.php/webdav/some/subdir/Tu_after_three.txt');
  746. });
  747. it('Triggers "fileActionsReady" event after rename', function() {
  748. var handler = sinon.stub();
  749. fileList.$fileList.on('fileActionsReady', handler);
  750. doRename();
  751. expect(handler.notCalled).toEqual(true);
  752. deferredRename.resolve(201);
  753. expect(handler.calledOnce).toEqual(true);
  754. expect(fileList.$fileList.find('.test').length).toEqual(0);
  755. });
  756. it('Leaves the summary alone when reinserting renamed element', function() {
  757. var $summary = $('#filestable .summary');
  758. doRename();
  759. deferredRename.resolve(201);
  760. expect($summary.find('.dirinfo').text()).toEqual('1 folder');
  761. expect($summary.find('.fileinfo').text()).toEqual('3 files');
  762. });
  763. it('Leaves the summary alone when cancel renaming', function() {
  764. var $summary = $('#filestable .summary');
  765. doCancelRename();
  766. expect($summary.find('.dirinfo').text()).toEqual('1 folder');
  767. expect($summary.find('.fileinfo').text()).toEqual('3 files');
  768. });
  769. it('Shows busy state while rename in progress', function() {
  770. var $tr;
  771. doRename();
  772. // element is renamed before the request finishes
  773. $tr = fileList.findFileEl('Tu_after_three.txt');
  774. expect($tr.length).toEqual(1);
  775. expect(fileList.findFileEl('One.txt').length).toEqual(0);
  776. // file actions are hidden
  777. expect($tr.hasClass('busy')).toEqual(true);
  778. // input and form are gone
  779. expect(fileList.$fileList.find('input.filename').length).toEqual(0);
  780. expect(fileList.$fileList.find('form').length).toEqual(0);
  781. });
  782. it('Validates the file name', function() {
  783. var $input, $tr;
  784. $tr = fileList.findFileEl('One.txt');
  785. expect($tr.find('a.name').css('display')).not.toEqual('none');
  786. // trigger rename prompt
  787. fileList.rename('One.txt');
  788. expect($tr.find('a.name .thumbnail-wrapper').css('display')).not.toEqual('none');
  789. expect($tr.find('a.name .nametext').css('display')).toEqual('none');
  790. $input = fileList.$fileList.find('input.filename');
  791. $input.val('Two.jpg');
  792. // simulate key to trigger validation
  793. $input.trigger(new $.Event('keyup', {keyCode: 97}));
  794. // input is still there with error
  795. expect(fileList.$fileList.find('input.filename').length).toEqual(1);
  796. expect(fileList.$fileList.find('input.filename').hasClass('error')).toEqual(true);
  797. // trigger submit does not send server request
  798. $input.closest('form').trigger('submit');
  799. expect(renameStub.notCalled).toEqual(true);
  800. // simulate escape key
  801. $input.trigger(new $.Event('keyup', {keyCode: 27}));
  802. // element is added back with the correct name
  803. $tr = fileList.findFileEl('One.txt');
  804. expect($tr.length).toEqual(1);
  805. expect($tr.find('a .nametext').text().trim()).toEqual('One.txt');
  806. expect($tr.find('a.name').css('display')).not.toEqual('none');
  807. $tr = fileList.findFileEl('Two.jpg');
  808. expect($tr.length).toEqual(1);
  809. expect($tr.find('a .nametext').text().trim()).toEqual('Two.jpg');
  810. expect($tr.find('a.name').css('display')).not.toEqual('none');
  811. // input and form are gone
  812. expect(fileList.$fileList.find('input.filename').length).toEqual(0);
  813. expect(fileList.$fileList.find('form').length).toEqual(0);
  814. });
  815. it('Restores thumbnail when rename was cancelled', function(done) {
  816. doRename();
  817. expect(fileList.findFileEl('Tu_after_three.txt').find('.thumbnail').parent().attr('class'))
  818. .toContain('icon-loading-small');
  819. deferredRename.reject(409);
  820. return Promise.resolve().then(function() {
  821. expect(fileList.findFileEl('One.txt').length).toEqual(1);
  822. expect(OC.TestUtil.getImageUrl(fileList.findFileEl('One.txt').find('.thumbnail')))
  823. .toEqual(OC.imagePath('core', 'filetypes/text.svg'));
  824. }).then(done, done);
  825. });
  826. });
  827. describe('Moving files', function() {
  828. var deferredMove;
  829. var moveStub;
  830. beforeEach(function() {
  831. deferredMove = $.Deferred();
  832. moveStub = sinon.stub(filesClient, 'move');
  833. fileList.setFiles(testFiles);
  834. });
  835. afterEach(function() {
  836. moveStub.restore();
  837. });
  838. it('Moves single file to target folder', function(done) {
  839. var promise = fileList.move('One.txt', '/somedir');
  840. moveStub.callsFake(function(src, dst){
  841. expect(moveStub.calledOnce).toEqual(true);
  842. expect(src).toEqual('/subdir/One.txt');
  843. expect(dst).toEqual('/somedir/One.txt');
  844. return deferredMove.promise();
  845. });
  846. deferredMove.resolve(201);
  847. return promise.then(function(){
  848. expect(fileList.findFileEl('One.txt').length).toEqual(0);
  849. // folder size has increased
  850. expect(fileList.findFileEl('somedir').data('size')).toEqual(262);
  851. expect(fileList.findFileEl('somedir').find('.filesize').text()).toEqual('262 B');
  852. expect(notificationStub.notCalled).toEqual(true);
  853. }).then(done, done);
  854. });
  855. it('Moves list of files to target folder', function(done) {
  856. var deferredMove1 = $.Deferred();
  857. var deferredMove2 = $.Deferred();
  858. moveStub.onCall(0).callsFake(function(src, dst){
  859. expect(moveStub.calledOnce).toEqual(true);
  860. expect(src).toEqual('/subdir/One.txt');
  861. expect(dst).toEqual('/somedir/One.txt');
  862. return deferredMove1.promise();
  863. });
  864. moveStub.onCall(1).callsFake(function(src, dst){
  865. expect(fileList.findFileEl('One.txt').length).toEqual(0);
  866. // folder size has increased during move
  867. expect(fileList.findFileEl('somedir').data('size')).toEqual(262);
  868. expect(fileList.findFileEl('somedir').find('.filesize').text()).toEqual('262 B');
  869. expect(src).toEqual('/subdir/Two.jpg');
  870. expect(dst).toEqual('/somedir/Two.jpg');
  871. return deferredMove2.promise();
  872. });
  873. var promise = fileList.move(['One.txt', 'Two.jpg'], '/somedir');
  874. deferredMove1.resolve(201);
  875. deferredMove2.resolve(201);
  876. return promise.then(function(){
  877. expect(moveStub.calledTwice).toEqual(true);
  878. expect(fileList.findFileEl('Two.jpg').length).toEqual(0);
  879. // folder size has increased
  880. expect(fileList.findFileEl('somedir').data('size')).toEqual(12311);
  881. expect(fileList.findFileEl('somedir').find('.filesize').text()).toEqual('12 KB');
  882. expect(notificationStub.notCalled).toEqual(true);
  883. }).then(done, done);
  884. });
  885. it('Shows notification if a file could not be moved', function(done) {
  886. moveStub.callsFake(function(){
  887. expect(moveStub.calledOnce).toEqual(true);
  888. return deferredMove.promise();
  889. });
  890. var promise = fileList.move('One.txt', '/somedir');
  891. deferredMove.reject(409);
  892. return promise.then(function(){
  893. expect(fileList.findFileEl('One.txt').length).toEqual(1);
  894. expect(notificationStub.calledOnce).toEqual(true);
  895. expect(notificationStub.getCall(0).args[0]).toEqual('Could not move "One.txt"');
  896. }).then(done, done);
  897. });
  898. it('Restores thumbnail if a file could not be moved', function(done) {
  899. moveStub.callsFake(function(){
  900. expect(fileList.findFileEl('One.txt').find('.thumbnail').parent().attr('class'))
  901. .toContain('icon-loading-small');
  902. expect(moveStub.calledOnce).toEqual(true);
  903. return deferredMove.promise();
  904. });
  905. var promise = fileList.move('One.txt', '/somedir');
  906. deferredMove.reject(409);
  907. return promise.then(function(){
  908. expect(fileList.findFileEl('One.txt').length).toEqual(1);
  909. expect(notificationStub.calledOnce).toEqual(true);
  910. expect(notificationStub.getCall(0).args[0]).toEqual('Could not move "One.txt"');
  911. expect(OC.TestUtil.getImageUrl(fileList.findFileEl('One.txt').find('.thumbnail')))
  912. .toEqual(OC.imagePath('core', 'filetypes/text.svg'));
  913. }).then(done, done);
  914. });
  915. });
  916. describe('Copying files', function() {
  917. var deferredCopy;
  918. var copyStub;
  919. beforeEach(function() {
  920. deferredCopy = $.Deferred();
  921. copyStub = sinon.stub(filesClient, 'copy');
  922. fileList.setFiles(testFiles);
  923. });
  924. afterEach(function() {
  925. copyStub.restore();
  926. });
  927. it('Copies single file to target folder', function(done) {
  928. copyStub.callsFake(function(){
  929. expect(copyStub.calledOnce).toEqual(true);
  930. expect(copyStub.getCall(0).args[0]).toEqual('/subdir/One.txt');
  931. expect(copyStub.getCall(0).args[1]).toEqual('/somedir/One.txt');
  932. return deferredCopy.promise();
  933. });
  934. var promise = fileList.copy('One.txt', '/somedir');
  935. deferredCopy.resolve(201);
  936. return promise.then(function(){
  937. // File is still here
  938. expect(fileList.findFileEl('One.txt').length).toEqual(1);
  939. // folder size has increased
  940. expect(fileList.findFileEl('somedir').data('size')).toEqual(262);
  941. expect(fileList.findFileEl('somedir').find('.filesize').text()).toEqual('262 B');
  942. // Copying sents a notification to tell that we've successfully copied file
  943. expect(notificationStub.notCalled).toEqual(false);
  944. }).then(done, done);
  945. });
  946. it('Copies list of files to target folder', function(done) {
  947. var deferredCopy1 = $.Deferred();
  948. var deferredCopy2 = $.Deferred();
  949. copyStub.onCall(0).callsFake(function(src, dst){
  950. expect(src).toEqual('/subdir/One.txt');
  951. expect(dst).toEqual('/somedir/One.txt');
  952. return deferredCopy1.promise();
  953. });
  954. copyStub.onCall(1).callsFake(function(src, dst){
  955. // folder size has increased during copy
  956. expect(fileList.findFileEl('somedir').data('size')).toEqual(262);
  957. expect(fileList.findFileEl('somedir').find('.filesize').text()).toEqual('262 B');
  958. expect(src).toEqual('/subdir/Two.jpg');
  959. expect(dst).toEqual('/somedir/Two.jpg');
  960. return deferredCopy2.promise();
  961. });
  962. var promise = fileList.copy(['One.txt', 'Two.jpg'], '/somedir');
  963. deferredCopy1.resolve(201);
  964. deferredCopy2.resolve(201);
  965. return promise.then(function(){
  966. expect(copyStub.calledTwice).toEqual(true);
  967. expect(fileList.findFileEl('Two.jpg').length).toEqual(1);
  968. expect(fileList.findFileEl('One.txt').length).toEqual(1);
  969. // folder size has increased
  970. expect(fileList.findFileEl('somedir').data('size')).toEqual(12311);
  971. expect(fileList.findFileEl('somedir').find('.filesize').text()).toEqual('12 KB');
  972. expect(notificationStub.notCalled).toEqual(false);
  973. }).then(done, done);
  974. });
  975. it('Shows notification if a file could not be copied', function(done) {
  976. copyStub.callsFake(function(){
  977. expect(copyStub.calledOnce).toEqual(true);
  978. return deferredCopy.promise();
  979. });
  980. var promise = fileList.copy('One.txt', '/somedir');
  981. deferredCopy.reject(409);
  982. return promise.then(function(){
  983. expect(fileList.findFileEl('One.txt').length).toEqual(1);
  984. expect(notificationStub.calledOnce).toEqual(true);
  985. expect(notificationStub.getCall(0).args[0]).toEqual('Could not copy "One.txt"');
  986. }).then(done, done);
  987. });
  988. it('Restores thumbnail if a file could not be copied', function(done) {
  989. copyStub.callsFake(function(){
  990. expect(fileList.findFileEl('One.txt').find('.thumbnail').parent().attr('class'))
  991. .toContain('icon-loading-small');
  992. expect(copyStub.calledOnce).toEqual(true);
  993. return deferredCopy.promise();
  994. });
  995. var promise = fileList.copy('One.txt', '/somedir');
  996. deferredCopy.reject(409);
  997. return promise.then(function(){
  998. expect(fileList.findFileEl('One.txt').length).toEqual(1);
  999. expect(notificationStub.calledOnce).toEqual(true);
  1000. expect(notificationStub.getCall(0).args[0]).toEqual('Could not copy "One.txt"');
  1001. expect(OC.TestUtil.getImageUrl(fileList.findFileEl('One.txt').find('.thumbnail')))
  1002. .toEqual(OC.imagePath('core', 'filetypes/text.svg'));
  1003. }).then(done, done);
  1004. });
  1005. });
  1006. describe('Update file', function() {
  1007. it('does not change summary', function() {
  1008. var $summary = $('#filestable .summary');
  1009. var fileData = new FileInfo({
  1010. type: 'file',
  1011. name: 'test file',
  1012. });
  1013. var $tr = fileList.add(fileData);
  1014. expect($summary.find('.dirinfo').hasClass('hidden')).toEqual(true);
  1015. expect($summary.find('.fileinfo').text()).toEqual('1 file');
  1016. var model = fileList.getModelForFile('test file');
  1017. model.set({size: '100'});
  1018. expect($summary.find('.dirinfo').hasClass('hidden')).toEqual(true);
  1019. expect($summary.find('.fileinfo').text()).toEqual('1 file');
  1020. });
  1021. })
  1022. describe('List rendering', function() {
  1023. it('renders a list of files using add()', function() {
  1024. expect(fileList.files.length).toEqual(0);
  1025. expect(fileList.files).toEqual([]);
  1026. fileList.setFiles(testFiles);
  1027. expect($('#fileList tr').length).toEqual(4);
  1028. expect(fileList.files.length).toEqual(4);
  1029. expect(fileList.files).toEqual(testFiles);
  1030. });
  1031. it('updates summary using the file sizes', function() {
  1032. var $summary;
  1033. fileList.setFiles(testFiles);
  1034. $summary = $('#filestable .summary');
  1035. expect($summary.hasClass('hidden')).toEqual(false);
  1036. expect($summary.find('.dirinfo').text()).toEqual('1 folder');
  1037. expect($summary.find('.fileinfo').text()).toEqual('3 files');
  1038. expect($summary.find('.filesize').text()).toEqual('69 KB');
  1039. });
  1040. it('shows headers, summary and hide empty content message after setting files', function(){
  1041. fileList.setFiles(testFiles);
  1042. expect($('#filestable thead th').hasClass('hidden')).toEqual(false);
  1043. expect($('#emptycontent').hasClass('hidden')).toEqual(true);
  1044. expect(fileList.$el.find('.summary').hasClass('hidden')).toEqual(false);
  1045. });
  1046. it('hides headers, summary and show empty content message after setting empty file list', function(){
  1047. fileList.setFiles([]);
  1048. expect($('#filestable thead th').hasClass('hidden')).toEqual(true);
  1049. expect($('#emptycontent').hasClass('hidden')).toEqual(false);
  1050. expect($('#emptycontent .uploadmessage').hasClass('hidden')).toEqual(false);
  1051. expect(fileList.$el.find('.summary').hasClass('hidden')).toEqual(true);
  1052. });
  1053. it('hides headers, upload message, and summary when list is empty and user has no creation permission', function(){
  1054. $('#permissions').val(0);
  1055. fileList.setFiles([]);
  1056. expect($('#filestable thead th').hasClass('hidden')).toEqual(true);
  1057. expect($('#emptycontent').hasClass('hidden')).toEqual(false);
  1058. expect($('#emptycontent .uploadmessage').hasClass('hidden')).toEqual(true);
  1059. expect(fileList.$el.find('.summary').hasClass('hidden')).toEqual(true);
  1060. });
  1061. it('calling findFileEl() can find existing file element', function() {
  1062. fileList.setFiles(testFiles);
  1063. expect(fileList.findFileEl('Two.jpg').length).toEqual(1);
  1064. });
  1065. it('calling findFileEl() returns empty when file not found in file', function() {
  1066. fileList.setFiles(testFiles);
  1067. expect(fileList.findFileEl('unexist.dat').length).toEqual(0);
  1068. });
  1069. it('only add file if in same current directory', function() {
  1070. $('#dir').val('/current dir');
  1071. var fileData = {
  1072. type: 'file',
  1073. name: 'testFile.txt',
  1074. directory: '/current dir'
  1075. };
  1076. fileList.add(fileData);
  1077. expect(fileList.findFileEl('testFile.txt').length).toEqual(1);
  1078. });
  1079. it('triggers "fileActionsReady" event after update', function() {
  1080. var handler = sinon.stub();
  1081. fileList.$fileList.on('fileActionsReady', handler);
  1082. fileList.setFiles(testFiles);
  1083. expect(handler.calledOnce).toEqual(true);
  1084. expect(handler.getCall(0).args[0].$files.length).toEqual(testFiles.length);
  1085. });
  1086. it('triggers "fileActionsReady" event after single add', function() {
  1087. var handler = sinon.stub();
  1088. var $tr;
  1089. fileList.setFiles(testFiles);
  1090. fileList.$fileList.on('fileActionsReady', handler);
  1091. $tr = fileList.add({name: 'test.txt'});
  1092. expect(handler.calledOnce).toEqual(true);
  1093. expect(handler.getCall(0).args[0].$files.is($tr)).toEqual(true);
  1094. });
  1095. it('triggers "fileActionsReady" event after next page load with the newly appended files', function() {
  1096. var handler = sinon.stub();
  1097. fileList.setFiles(generateFiles(0, 64));
  1098. fileList.$fileList.on('fileActionsReady', handler);
  1099. fileList._nextPage();
  1100. expect(handler.calledOnce).toEqual(true);
  1101. expect(handler.getCall(0).args[0].$files.length).toEqual(fileList.pageSize());
  1102. });
  1103. it('does not trigger "fileActionsReady" event after single add with silent argument', function() {
  1104. var handler = sinon.stub();
  1105. fileList.setFiles(testFiles);
  1106. fileList.$fileList.on('fileActionsReady', handler);
  1107. fileList.add({name: 'test.txt'}, {silent: true});
  1108. expect(handler.notCalled).toEqual(true);
  1109. });
  1110. it('triggers "updated" event after update', function() {
  1111. var handler = sinon.stub();
  1112. fileList.$fileList.on('updated', handler);
  1113. fileList.setFiles(testFiles);
  1114. expect(handler.calledOnce).toEqual(true);
  1115. });
  1116. it('does not update summary when removing non-existing files', function() {
  1117. var $summary;
  1118. // single file
  1119. fileList.setFiles([testFiles[0]]);
  1120. $summary = $('#filestable .summary');
  1121. expect($summary.hasClass('hidden')).toEqual(false);
  1122. expect($summary.find('.dirinfo').hasClass('hidden')).toEqual(true);
  1123. expect($summary.find('.fileinfo').text()).toEqual('1 file');
  1124. fileList.remove('unexist.txt');
  1125. expect($summary.hasClass('hidden')).toEqual(false);
  1126. expect($summary.find('.dirinfo').hasClass('hidden')).toEqual(true);
  1127. expect($summary.find('.fileinfo').text()).toEqual('1 file');
  1128. });
  1129. });
  1130. describe('Filtered list rendering', function() {
  1131. it('filters the list of files using filter()', function() {
  1132. expect(fileList.files.length).toEqual(0);
  1133. expect(fileList.files).toEqual([]);
  1134. fileList.setFiles(testFiles);
  1135. var $summary = $('#filestable .summary');
  1136. var $nofilterresults = fileList.$el.find(".nofilterresults");
  1137. expect($nofilterresults.length).toEqual(1);
  1138. expect($summary.hasClass('hidden')).toEqual(false);
  1139. expect($('#fileList tr:not(.hidden)').length).toEqual(4);
  1140. expect(fileList.files.length).toEqual(4);
  1141. expect($summary.hasClass('hidden')).toEqual(false);
  1142. expect($nofilterresults.hasClass('hidden')).toEqual(true);
  1143. fileList.setFilter('e');
  1144. expect($('#fileList tr:not(.hidden)').length).toEqual(3);
  1145. expect(fileList.files.length).toEqual(4);
  1146. expect($summary.hasClass('hidden')).toEqual(false);
  1147. expect($summary.find('.dirinfo').text()).toEqual('1 folder');
  1148. expect($summary.find('.fileinfo').text()).toEqual('2 files');
  1149. expect($summary.find('.filter').text()).toEqual(" match \"e\"");
  1150. expect($nofilterresults.hasClass('hidden')).toEqual(true);
  1151. fileList.setFilter('ee');
  1152. expect($('#fileList tr:not(.hidden)').length).toEqual(1);
  1153. expect(fileList.files.length).toEqual(4);
  1154. expect($summary.hasClass('hidden')).toEqual(false);
  1155. expect($summary.find('.dirinfo').hasClass('hidden')).toEqual(true);
  1156. expect($summary.find('.fileinfo').text()).toEqual('1 file');
  1157. expect($summary.find('.filter').text()).toEqual(" matches \"ee\"");
  1158. expect($nofilterresults.hasClass('hidden')).toEqual(true);
  1159. fileList.setFilter('eee');
  1160. expect($('#fileList tr:not(.hidden)').length).toEqual(0);
  1161. expect(fileList.files.length).toEqual(4);
  1162. expect($summary.hasClass('hidden')).toEqual(true);
  1163. expect($nofilterresults.hasClass('hidden')).toEqual(false);
  1164. fileList.setFilter('ee');
  1165. expect($('#fileList tr:not(.hidden)').length).toEqual(1);
  1166. expect(fileList.files.length).toEqual(4);
  1167. expect($summary.hasClass('hidden')).toEqual(false);
  1168. expect($summary.find('.dirinfo').hasClass('hidden')).toEqual(true);
  1169. expect($summary.find('.fileinfo').text()).toEqual('1 file');
  1170. expect($summary.find('.filter').text()).toEqual(" matches \"ee\"");
  1171. expect($nofilterresults.hasClass('hidden')).toEqual(true);
  1172. fileList.setFilter('e');
  1173. expect($('#fileList tr:not(.hidden)').length).toEqual(3);
  1174. expect(fileList.files.length).toEqual(4);
  1175. expect($summary.hasClass('hidden')).toEqual(false);
  1176. expect($summary.find('.dirinfo').text()).toEqual('1 folder');
  1177. expect($summary.find('.fileinfo').text()).toEqual('2 files');
  1178. expect($summary.find('.filter').text()).toEqual(" match \"e\"");
  1179. expect($nofilterresults.hasClass('hidden')).toEqual(true);
  1180. fileList.setFilter('');
  1181. expect($('#fileList tr:not(.hidden)').length).toEqual(4);
  1182. expect(fileList.files.length).toEqual(4);
  1183. expect($summary.hasClass('hidden')).toEqual(false);
  1184. expect($summary.find('.dirinfo').text()).toEqual('1 folder');
  1185. expect($summary.find('.fileinfo').text()).toEqual('3 files');
  1186. expect($nofilterresults.hasClass('hidden')).toEqual(true);
  1187. });
  1188. it('filters the list of non-rendered rows using filter()', function() {
  1189. var $summary = $('#filestable .summary');
  1190. var $nofilterresults = fileList.$el.find(".nofilterresults");
  1191. fileList.setFiles(generateFiles(0, 64));
  1192. fileList.setFilter('63');
  1193. expect($('#fileList tr:not(.hidden)').length).toEqual(1);
  1194. expect($summary.hasClass('hidden')).toEqual(false);
  1195. expect($summary.find('.dirinfo').hasClass('hidden')).toEqual(true);
  1196. expect($summary.find('.fileinfo').text()).toEqual('1 file');
  1197. expect($summary.find('.filter').text()).toEqual(" matches \"63\"");
  1198. expect($nofilterresults.hasClass('hidden')).toEqual(true);
  1199. });
  1200. it('hides the emptyfiles notice when using filter()', function() {
  1201. expect(fileList.files.length).toEqual(0);
  1202. expect(fileList.files).toEqual([]);
  1203. fileList.setFiles([]);
  1204. var $summary = $('#filestable .summary');
  1205. var $emptycontent = fileList.$el.find("#emptycontent");
  1206. var $nofilterresults = fileList.$el.find(".nofilterresults");
  1207. expect($emptycontent.length).toEqual(1);
  1208. expect($nofilterresults.length).toEqual(1);
  1209. expect($('#fileList tr:not(.hidden)').length).toEqual(0);
  1210. expect(fileList.files.length).toEqual(0);
  1211. expect($summary.hasClass('hidden')).toEqual(true);
  1212. expect($emptycontent.hasClass('hidden')).toEqual(false);
  1213. expect($nofilterresults.hasClass('hidden')).toEqual(true);
  1214. fileList.setFilter('e');
  1215. expect($('#fileList tr:not(.hidden)').length).toEqual(0);
  1216. expect(fileList.files.length).toEqual(0);
  1217. expect($summary.hasClass('hidden')).toEqual(true);
  1218. expect($emptycontent.hasClass('hidden')).toEqual(true);
  1219. expect($nofilterresults.hasClass('hidden')).toEqual(false);
  1220. fileList.setFilter('');
  1221. expect($('#fileList tr:not(.hidden)').length).toEqual(0);
  1222. expect(fileList.files.length).toEqual(0);
  1223. expect($summary.hasClass('hidden')).toEqual(true);
  1224. expect($emptycontent.hasClass('hidden')).toEqual(false);
  1225. expect($nofilterresults.hasClass('hidden')).toEqual(true);
  1226. });
  1227. it('does not show the emptyfiles or nofilterresults notice when the mask is active', function() {
  1228. expect(fileList.files.length).toEqual(0);
  1229. expect(fileList.files).toEqual([]);
  1230. fileList.showMask();
  1231. fileList.setFiles(testFiles);
  1232. var $emptycontent = fileList.$el.find("#emptycontent");
  1233. var $nofilterresults = fileList.$el.find(".nofilterresults");
  1234. expect($emptycontent.length).toEqual(1);
  1235. expect($nofilterresults.length).toEqual(1);
  1236. expect($emptycontent.hasClass('hidden')).toEqual(true);
  1237. expect($nofilterresults.hasClass('hidden')).toEqual(true);
  1238. /*
  1239. fileList.setFilter('e');
  1240. expect($emptycontent.hasClass('hidden')).toEqual(true);
  1241. expect($nofilterresults.hasClass('hidden')).toEqual(false);
  1242. */
  1243. fileList.setFilter('');
  1244. expect($emptycontent.hasClass('hidden')).toEqual(true);
  1245. expect($nofilterresults.hasClass('hidden')).toEqual(true);
  1246. });
  1247. });
  1248. describe('Rendering next page on scroll', function() {
  1249. beforeEach(function() {
  1250. fileList.setFiles(generateFiles(0, 64));
  1251. });
  1252. it('renders only the first page', function() {
  1253. expect(fileList.files.length).toEqual(65);
  1254. expect($('#fileList tr').length).toEqual(20);
  1255. });
  1256. it('renders the full first page despite hidden rows', function() {
  1257. filesConfig.set('showhidden', false);
  1258. var files = _.map(generateFiles(0, 23), function(data) {
  1259. return _.extend(data, {
  1260. name: '.' + data.name
  1261. });
  1262. });
  1263. // only hidden files + one visible
  1264. files.push(testFiles[0]);
  1265. fileList.setFiles(files);
  1266. expect(fileList.files.length).toEqual(25);
  1267. // render 24 hidden elements + the visible one
  1268. expect($('#fileList tr').length).toEqual(25);
  1269. });
  1270. it('renders the full first page despite hidden rows', function() {
  1271. filesConfig.set('showhidden', true);
  1272. var files = _.map(generateFiles(0, 23), function(data) {
  1273. return _.extend(data, {
  1274. name: '.' + data.name
  1275. });
  1276. });
  1277. // only hidden files + one visible
  1278. files.push(testFiles[0]);
  1279. fileList.setFiles(files);
  1280. expect(fileList.files.length).toEqual(25);
  1281. // render 20 first hidden elements as visible
  1282. expect($('#fileList tr').length).toEqual(20);
  1283. });
  1284. it('renders the second page when scrolling down (trigger nextPage)', function() {
  1285. // TODO: can't simulate scrolling here, so calling nextPage directly
  1286. fileList._nextPage(true);
  1287. expect($('#fileList tr').length).toEqual(40);
  1288. fileList._nextPage(true);
  1289. expect($('#fileList tr').length).toEqual(60);
  1290. fileList._nextPage(true);
  1291. expect($('#fileList tr').length).toEqual(65);
  1292. fileList._nextPage(true);
  1293. // stays at 65
  1294. expect($('#fileList tr').length).toEqual(65);
  1295. });
  1296. it('inserts into the DOM if insertion point is in the visible page ', function() {
  1297. fileList.add({
  1298. id: 2000,
  1299. type: 'file',
  1300. name: 'File with index 15b.txt'
  1301. });
  1302. expect($('#fileList tr').length).toEqual(21);
  1303. expect(fileList.findFileEl('File with index 15b.txt').index()).toEqual(16);
  1304. });
  1305. it('does not inserts into the DOM if insertion point is not the visible page ', function() {
  1306. fileList.add({
  1307. id: 2000,
  1308. type: 'file',
  1309. name: 'File with index 28b.txt'
  1310. });
  1311. expect($('#fileList tr').length).toEqual(20);
  1312. expect(fileList.findFileEl('File with index 28b.txt').length).toEqual(0);
  1313. fileList._nextPage(true);
  1314. expect($('#fileList tr').length).toEqual(40);
  1315. expect(fileList.findFileEl('File with index 28b.txt').index()).toEqual(29);
  1316. });
  1317. it('appends into the DOM when inserting a file after the last visible element', function() {
  1318. fileList.add({
  1319. id: 2000,
  1320. type: 'file',
  1321. name: 'File with index 19b.txt'
  1322. });
  1323. expect($('#fileList tr').length).toEqual(21);
  1324. fileList._nextPage(true);
  1325. expect($('#fileList tr').length).toEqual(41);
  1326. });
  1327. it('appends into the DOM when inserting a file on the last page when visible', function() {
  1328. fileList._nextPage(true);
  1329. expect($('#fileList tr').length).toEqual(40);
  1330. fileList._nextPage(true);
  1331. expect($('#fileList tr').length).toEqual(60);
  1332. fileList._nextPage(true);
  1333. expect($('#fileList tr').length).toEqual(65);
  1334. fileList._nextPage(true);
  1335. fileList.add({
  1336. id: 2000,
  1337. type: 'file',
  1338. name: 'File with index 88.txt'
  1339. });
  1340. expect($('#fileList tr').length).toEqual(66);
  1341. fileList._nextPage(true);
  1342. expect($('#fileList tr').length).toEqual(66);
  1343. });
  1344. it('shows additional page when appending a page of files and scrolling down', function() {
  1345. var newFiles = generateFiles(66, 81);
  1346. for (var i = 0; i < newFiles.length; i++) {
  1347. fileList.add(newFiles[i]);
  1348. }
  1349. expect($('#fileList tr').length).toEqual(20);
  1350. fileList._nextPage(true);
  1351. expect($('#fileList tr').length).toEqual(40);
  1352. fileList._nextPage(true);
  1353. expect($('#fileList tr').length).toEqual(60);
  1354. fileList._nextPage(true);
  1355. expect($('#fileList tr').length).toEqual(80);
  1356. fileList._nextPage(true);
  1357. expect($('#fileList tr').length).toEqual(81);
  1358. fileList._nextPage(true);
  1359. expect($('#fileList tr').length).toEqual(81);
  1360. });
  1361. it('automatically renders next page when there are not enough elements visible', function() {
  1362. // delete the 15 first elements
  1363. for (var i = 0; i < 15; i++) {
  1364. fileList.remove(fileList.files[0].name);
  1365. }
  1366. // still makes sure that there are 20 elements visible, if any
  1367. expect($('#fileList tr').length).toEqual(25);
  1368. });
  1369. });
  1370. describe('file previews', function() {
  1371. var previewLoadStub;
  1372. beforeEach(function() {
  1373. previewLoadStub = sinon.stub(OCA.Files.FileList.prototype, 'lazyLoadPreview');
  1374. });
  1375. afterEach(function() {
  1376. previewLoadStub.restore();
  1377. });
  1378. it('renders default file icon when none provided and no mime type is set', function() {
  1379. var fileData = {
  1380. name: 'testFile.txt'
  1381. };
  1382. var $tr = fileList.add(fileData);
  1383. var $imgDiv = $tr.find('td.filename .thumbnail');
  1384. expect(OC.TestUtil.getImageUrl($imgDiv)).toEqual(OC.getRootPath() + '/core/img/filetypes/file.svg');
  1385. // tries to load preview
  1386. expect(previewLoadStub.calledOnce).toEqual(true);
  1387. });
  1388. it('renders default icon for folder when none provided', function() {
  1389. var fileData = {
  1390. name: 'test dir',
  1391. mimetype: 'httpd/unix-directory'
  1392. };
  1393. var $tr = fileList.add(fileData);
  1394. var $imgDiv = $tr.find('td.filename .thumbnail');
  1395. expect(OC.TestUtil.getImageUrl($imgDiv)).toEqual(OC.getRootPath() + '/core/img/filetypes/folder.svg');
  1396. // no preview since it's a directory
  1397. expect(previewLoadStub.notCalled).toEqual(true);
  1398. });
  1399. it('renders provided icon for file when provided', function() {
  1400. var fileData = new FileInfo({
  1401. type: 'file',
  1402. name: 'test file',
  1403. icon: OC.getRootPath() + '/core/img/filetypes/application-pdf.svg',
  1404. mimetype: 'application/pdf'
  1405. });
  1406. var $tr = fileList.add(fileData);
  1407. var $imgDiv = $tr.find('td.filename .thumbnail');
  1408. expect(OC.TestUtil.getImageUrl($imgDiv)).toEqual(OC.getRootPath() + '/core/img/filetypes/application-pdf.svg');
  1409. // try loading preview
  1410. expect(previewLoadStub.calledOnce).toEqual(true);
  1411. });
  1412. it('renders provided icon for file when provided', function() {
  1413. var fileData = new FileInfo({
  1414. name: 'somefile.pdf',
  1415. icon: OC.getRootPath() + '/core/img/filetypes/application-pdf.svg'
  1416. });
  1417. var $tr = fileList.add(fileData);
  1418. var $imgDiv = $tr.find('td.filename .thumbnail');
  1419. expect(OC.TestUtil.getImageUrl($imgDiv)).toEqual(OC.getRootPath() + '/core/img/filetypes/application-pdf.svg');
  1420. // try loading preview
  1421. expect(previewLoadStub.calledOnce).toEqual(true);
  1422. });
  1423. it('renders provided icon for folder when provided', function() {
  1424. var fileData = new FileInfo({
  1425. name: 'some folder',
  1426. mimetype: 'httpd/unix-directory',
  1427. icon: OC.getRootPath() + '/core/img/filetypes/folder-alt.svg'
  1428. });
  1429. var $tr = fileList.add(fileData);
  1430. var $imgDiv = $tr.find('td.filename .thumbnail');
  1431. expect(OC.TestUtil.getImageUrl($imgDiv)).toEqual(OC.getRootPath() + '/core/img/filetypes/folder-alt.svg');
  1432. // do not load preview for folders
  1433. expect(previewLoadStub.notCalled).toEqual(true);
  1434. });
  1435. it('renders preview when no icon was provided', function() {
  1436. var fileData = {
  1437. type: 'file',
  1438. name: 'test file'
  1439. };
  1440. var $tr = fileList.add(fileData);
  1441. var $td = $tr.find('td.filename');
  1442. expect(OC.TestUtil.getImageUrl($td.find('.thumbnail')))
  1443. .toEqual(OC.getRootPath() + '/core/img/filetypes/file.svg');
  1444. expect(previewLoadStub.calledOnce).toEqual(true);
  1445. // third argument is callback
  1446. previewLoadStub.getCall(0).args[0].callback(OC.getRootPath() + '/somepath.png');
  1447. expect(OC.TestUtil.getImageUrl($td.find('.thumbnail'))).toEqual(OC.getRootPath() + '/somepath.png');
  1448. });
  1449. it('does not render preview for directories', function() {
  1450. var fileData = {
  1451. type: 'dir',
  1452. mimetype: 'httpd/unix-directory',
  1453. name: 'test dir'
  1454. };
  1455. var $tr = fileList.add(fileData);
  1456. var $td = $tr.find('td.filename');
  1457. expect(OC.TestUtil.getImageUrl($td.find('.thumbnail'))).toEqual(OC.getRootPath() + '/core/img/filetypes/folder.svg');
  1458. expect(previewLoadStub.notCalled).toEqual(true);
  1459. });
  1460. it('render encrypted folder icon for encrypted root', function() {
  1461. var fileData = {
  1462. type: 'dir',
  1463. mimetype: 'httpd/unix-directory',
  1464. name: 'test dir',
  1465. isEncrypted: true
  1466. };
  1467. var $tr = fileList.add(fileData);
  1468. var $td = $tr.find('td.filename');
  1469. expect(OC.TestUtil.getImageUrl($td.find('.thumbnail'))).toEqual(OC.getRootPath() + '/core/img/filetypes/folder-encrypted.svg');
  1470. expect(previewLoadStub.notCalled).toEqual(true);
  1471. });
  1472. it('render encrypted folder icon for encrypted subdir', function() {
  1473. var fileData = {
  1474. type: 'dir',
  1475. mimetype: 'httpd/unix-directory',
  1476. name: 'test dir',
  1477. isEncrypted: true
  1478. };
  1479. var $tr = fileList.add(fileData);
  1480. var $td = $tr.find('td.filename');
  1481. expect(OC.TestUtil.getImageUrl($td.find('.thumbnail'))).toEqual(OC.getRootPath() + '/core/img/filetypes/folder-encrypted.svg');
  1482. expect(previewLoadStub.notCalled).toEqual(true);
  1483. // default icon override
  1484. expect($tr.attr('data-icon')).toEqual(OC.getRootPath() + '/core/img/filetypes/folder-encrypted.svg');
  1485. });
  1486. it('render external storage icon for external storage root', function() {
  1487. var fileData = {
  1488. type: 'dir',
  1489. mimetype: 'httpd/unix-directory',
  1490. name: 'test dir',
  1491. mountType: 'external-root'
  1492. };
  1493. var $tr = fileList.add(fileData);
  1494. var $td = $tr.find('td.filename');
  1495. expect(OC.TestUtil.getImageUrl($td.find('.thumbnail'))).toEqual(OC.getRootPath() + '/core/img/filetypes/folder-external.svg');
  1496. expect(previewLoadStub.notCalled).toEqual(true);
  1497. });
  1498. it('render external storage icon for external storage subdir', function() {
  1499. var fileData = {
  1500. type: 'dir',
  1501. mimetype: 'httpd/unix-directory',
  1502. name: 'test dir',
  1503. mountType: 'external'
  1504. };
  1505. var $tr = fileList.add(fileData);
  1506. var $td = $tr.find('td.filename');
  1507. expect(OC.TestUtil.getImageUrl($td.find('.thumbnail'))).toEqual(OC.getRootPath() + '/core/img/filetypes/folder-external.svg');
  1508. expect(previewLoadStub.notCalled).toEqual(true);
  1509. // default icon override
  1510. expect($tr.attr('data-icon')).toEqual(OC.getRootPath() + '/core/img/filetypes/folder-external.svg');
  1511. });
  1512. });
  1513. describe('viewer mode', function() {
  1514. it('enabling viewer mode hides files table and action buttons', function() {
  1515. fileList.setViewerMode(true);
  1516. expect($('#filestable').hasClass('hidden')).toEqual(true);
  1517. expect($('.actions').hasClass('hidden')).toEqual(true);
  1518. expect($('.notCreatable').hasClass('hidden')).toEqual(true);
  1519. });
  1520. it('disabling viewer mode restores files table and action buttons', function() {
  1521. fileList.setViewerMode(true);
  1522. fileList.setViewerMode(false);
  1523. expect($('#filestable').hasClass('hidden')).toEqual(false);
  1524. expect($('.actions').hasClass('hidden')).toEqual(false);
  1525. expect($('.notCreatable').hasClass('hidden')).toEqual(true);
  1526. });
  1527. it('disabling viewer mode restores files table and action buttons with correct permissions', function() {
  1528. $('#permissions').val(0);
  1529. fileList.setViewerMode(true);
  1530. fileList.setViewerMode(false);
  1531. expect($('#filestable').hasClass('hidden')).toEqual(false);
  1532. expect($('.actions').hasClass('hidden')).toEqual(true);
  1533. expect($('.notCreatable').hasClass('hidden')).toEqual(false);
  1534. });
  1535. it('toggling viewer mode triggers event', function() {
  1536. var handler = sinon.stub();
  1537. fileList.$el.on('changeViewerMode', handler);
  1538. fileList.setViewerMode(true);
  1539. expect(handler.calledOnce).toEqual(true);
  1540. expect(handler.getCall(0).args[0].viewerModeEnabled).toEqual(true);
  1541. handler.reset();
  1542. fileList.setViewerMode(false);
  1543. expect(handler.calledOnce).toEqual(true);
  1544. expect(handler.getCall(0).args[0].viewerModeEnabled).toEqual(false);
  1545. });
  1546. });
  1547. describe('loading file list', function() {
  1548. var deferredList;
  1549. var getFolderContentsStub;
  1550. beforeEach(function() {
  1551. deferredList = $.Deferred();
  1552. getFolderContentsStub = sinon.stub(filesClient, 'getFolderContents').returns(deferredList.promise());
  1553. });
  1554. afterEach(function() {
  1555. getFolderContentsStub.restore();
  1556. });
  1557. it('fetches file list from server and renders it when reload() is called', function(done) {
  1558. var reloading = fileList.reload();
  1559. expect(getFolderContentsStub.calledOnce).toEqual(true);
  1560. expect(getFolderContentsStub.calledWith('/subdir')).toEqual(true);
  1561. deferredList.resolve(200, [testRoot].concat(testFiles));
  1562. return reloading.then(function() {
  1563. expect($('#fileList tr').length).toEqual(4);
  1564. expect(fileList.findFileEl('One.txt').length).toEqual(1);
  1565. }).then(done, done);
  1566. });
  1567. it('switches dir and fetches file list when calling changeDirectory()', function() {
  1568. fileList.changeDirectory('/anothersubdir');
  1569. expect(fileList.getCurrentDirectory()).toEqual('/anothersubdir');
  1570. expect(getFolderContentsStub.calledOnce).toEqual(true);
  1571. expect(getFolderContentsStub.calledWith('/anothersubdir')).toEqual(true);
  1572. });
  1573. it('converts backslashes to slashes when calling changeDirectory()', function() {
  1574. fileList.changeDirectory('/another\\subdir');
  1575. expect(fileList.getCurrentDirectory()).toEqual('/another/subdir');
  1576. });
  1577. it('switches to root dir when current directory is invalid', function() {
  1578. _.each([
  1579. '..',
  1580. '/..',
  1581. '../',
  1582. '/../',
  1583. '/../abc',
  1584. '/abc/..',
  1585. '/abc/../',
  1586. '/../abc/',
  1587. '/foo%0Abar/',
  1588. '/foo%00bar/',
  1589. '/another\\subdir/../foo\\../bar\\..\\file/..\\folder/../'
  1590. ], function(path) {
  1591. fileList.changeDirectory(decodeURI(path));
  1592. expect(fileList.getCurrentDirectory()).toEqual('/');
  1593. });
  1594. });
  1595. it('allows paths with dotdot at the beginning or end', function() {
  1596. _.each([
  1597. '/..abc',
  1598. '/def..',
  1599. '/...',
  1600. '/abc../def'
  1601. ], function(path) {
  1602. fileList.changeDirectory(path);
  1603. expect(fileList.getCurrentDirectory()).toEqual(path);
  1604. });
  1605. });
  1606. it('switches to root dir when current directory does not exist', function(done) {
  1607. var changing = fileList.changeDirectory('/unexist');
  1608. deferredList.reject(404);
  1609. return changing.then(function() {
  1610. expect(fileList.getCurrentDirectory()).toEqual('/');
  1611. }).then(done, done);
  1612. });
  1613. it('switches to root dir when current directory returns 400', function(done) {
  1614. var changing = fileList.changeDirectory('/unexist');
  1615. deferredList.reject(400);
  1616. return changing.then(function() {
  1617. expect(fileList.getCurrentDirectory()).toEqual('/');
  1618. }).then(done, done);
  1619. });
  1620. it('switches to root dir when current directory returns 405', function(done) {
  1621. var changing = fileList.changeDirectory('/unexist');
  1622. deferredList.reject(405);
  1623. return changing.then(function() {
  1624. expect(fileList.getCurrentDirectory()).toEqual('/');
  1625. }).then(done, done);
  1626. });
  1627. it('switches to root dir when current directory is forbidden', function(done) {
  1628. var changing = fileList.changeDirectory('/unexist');
  1629. deferredList.reject(403);
  1630. return changing.then(function() {
  1631. expect(fileList.getCurrentDirectory()).toEqual('/');
  1632. }).then(done, done);
  1633. });
  1634. it('switches to root dir when current directory is unavailable', function(done) {
  1635. var changing = fileList.changeDirectory('/unexist');
  1636. deferredList.reject(500);
  1637. return changing.then(function() {
  1638. expect(fileList.getCurrentDirectory()).toEqual('/');
  1639. }).then(done, done);
  1640. });
  1641. it('shows mask before loading file list then hides it at the end', function(done) {
  1642. var showMaskStub = sinon.stub(fileList, 'showMask');
  1643. var hideMaskStub = sinon.stub(fileList, 'hideMask');
  1644. var changing = fileList.changeDirectory('/anothersubdir');
  1645. expect(showMaskStub.calledOnce).toEqual(true);
  1646. expect(hideMaskStub.calledOnce).toEqual(false);
  1647. deferredList.resolve(200, [testRoot].concat(testFiles));
  1648. return changing.then(function() {
  1649. expect(showMaskStub.calledOnce).toEqual(true);
  1650. expect(hideMaskStub.calledOnce).toEqual(true);
  1651. showMaskStub.restore();
  1652. hideMaskStub.restore();
  1653. }).then(done, done);
  1654. });
  1655. it('triggers "changeDirectory" event when changing directory', function(done) {
  1656. var handler = sinon.stub();
  1657. $('#app-content-files').on('changeDirectory', handler);
  1658. var changing = fileList.changeDirectory('/somedir');
  1659. deferredList.resolve(200, [testRoot].concat(testFiles));
  1660. return changing.then(function() {
  1661. expect(handler.calledOnce).toEqual(true);
  1662. expect(handler.getCall(0).args[0].dir).toEqual('/somedir');
  1663. }).then(done, done);
  1664. });
  1665. it('triggers "afterChangeDirectory" event with fileid after changing directory', function(done) {
  1666. var handler = sinon.stub();
  1667. $('#app-content-files').on('afterChangeDirectory', handler);
  1668. var changing = fileList.changeDirectory('/somedir');
  1669. deferredList.resolve(200, [testRoot].concat(testFiles));
  1670. return changing.then(function() {
  1671. expect(handler.calledOnce).toEqual(true);
  1672. expect(handler.getCall(0).args[0].dir).toEqual('/somedir');
  1673. expect(handler.getCall(0).args[0].fileId).toEqual(99);
  1674. }).then(done, done);
  1675. });
  1676. it('changes the directory when receiving "urlChanged" event', function() {
  1677. $('#app-content-files').trigger(new $.Event('urlChanged', {view: 'files', dir: '/somedir'}));
  1678. expect(fileList.getCurrentDirectory()).toEqual('/somedir');
  1679. });
  1680. it('reloads the list when leaving hidden state', function() {
  1681. var reloadStub = sinon.stub(fileList, 'reload');
  1682. // First show should not trigger
  1683. $('#app-content-files').trigger(new $.Event('show'));
  1684. expect(reloadStub.calledOnce).toEqual(false);
  1685. // Second show should!
  1686. $('#app-content-files').trigger(new $.Event('show'));
  1687. expect(reloadStub.calledOnce).toEqual(true);
  1688. reloadStub.restore();
  1689. });
  1690. it('refreshes breadcrumb after update', function() {
  1691. var setDirSpy = sinon.spy(fileList.breadcrumb, 'setDirectory');
  1692. fileList.changeDirectory('/anothersubdir');
  1693. deferredList.resolve(200, [testRoot].concat(testFiles));
  1694. // twice because setDirectory gets called by _setCurrentDir which
  1695. // gets called directly by changeDirectory and via reload()
  1696. expect(fileList.breadcrumb.setDirectory.calledTwice).toEqual(true);
  1697. expect(fileList.breadcrumb.setDirectory.calledWith('/anothersubdir')).toEqual(true);
  1698. setDirSpy.restore();
  1699. getFolderContentsStub.restore();
  1700. });
  1701. it('prepends a slash to directory if none was given', function() {
  1702. fileList.changeDirectory('');
  1703. expect(fileList.getCurrentDirectory()).toEqual('/');
  1704. fileList.changeDirectory('noslash');
  1705. expect(fileList.getCurrentDirectory()).toEqual('/noslash');
  1706. });
  1707. });
  1708. describe('breadcrumb events', function() {
  1709. var deferredList;
  1710. var getFolderContentsStub;
  1711. beforeEach(function() {
  1712. deferredList = $.Deferred();
  1713. getFolderContentsStub = sinon.stub(filesClient, 'getFolderContents').returns(deferredList.promise());
  1714. });
  1715. afterEach(function() {
  1716. getFolderContentsStub.restore();
  1717. });
  1718. it('clicking on root breadcrumb changes directory to root', function() {
  1719. fileList.changeDirectory('/subdir/two/three with space/four/five');
  1720. deferredList.resolve(200, [testRoot].concat(testFiles));
  1721. var changeDirStub = sinon.stub(fileList, 'changeDirectory');
  1722. fileList.breadcrumb.$el.find('.crumb:eq(1) > a').trigger({type: 'click', which: 1});
  1723. expect(changeDirStub.calledOnce).toEqual(true);
  1724. expect(changeDirStub.getCall(0).args[0]).toEqual('/');
  1725. changeDirStub.restore();
  1726. });
  1727. it('clicking on breadcrumb changes directory', function() {
  1728. fileList.changeDirectory('/subdir/two/three with space/four/five');
  1729. deferredList.resolve(200, [testRoot].concat(testFiles));
  1730. var changeDirStub = sinon.stub(fileList, 'changeDirectory');
  1731. fileList.breadcrumb.$el.find('.crumb:eq(4) > a').trigger({type: 'click', which: 1});
  1732. expect(changeDirStub.calledOnce).toEqual(true);
  1733. expect(changeDirStub.getCall(0).args[0]).toEqual('/subdir/two/three with space');
  1734. changeDirStub.restore();
  1735. });
  1736. it('dropping files on breadcrumb calls move operation', function() {
  1737. var testDir = '/subdir/two/three with space/four/five';
  1738. var moveStub = sinon.stub(filesClient, 'move');
  1739. var deferredMove1 = $.Deferred();
  1740. var deferredMove2 = $.Deferred();
  1741. moveStub.onCall(0).returns(deferredMove1.promise());
  1742. moveStub.onCall(1).returns(deferredMove2.promise());
  1743. fileList.changeDirectory(testDir);
  1744. deferredList.resolve(200, [testRoot].concat(testFiles));
  1745. var $crumb = fileList.breadcrumb.$el.find('.crumb:eq(4)');
  1746. // no idea what this is but is required by the handler
  1747. var ui = {
  1748. helper: {
  1749. find: sinon.stub()
  1750. }
  1751. };
  1752. // returns a list of tr that were dragged
  1753. ui.helper.find.returns([
  1754. $('<tr data-file="One.txt" data-dir="' + testDir + '"></tr>'),
  1755. $('<tr data-file="Two.jpg" data-dir="' + testDir + '"></tr>')
  1756. ]);
  1757. // simulate drop event
  1758. var result = fileList._onDropOnBreadCrumb(new $.Event('drop', {target: $crumb}), ui).then(function(){
  1759. expect(moveStub.callCount).toEqual(2);
  1760. expect(moveStub.getCall(0).args[0]).toEqual(testDir + '/One.txt');
  1761. expect(moveStub.getCall(0).args[1]).toEqual('/subdir/two/three with space/One.txt');
  1762. expect(moveStub.getCall(1).args[0]).toEqual(testDir + '/Two.jpg');
  1763. expect(moveStub.getCall(1).args[1]).toEqual('/subdir/two/three with space/Two.jpg');
  1764. moveStub.restore();
  1765. });
  1766. deferredMove1.resolve(201);
  1767. deferredMove2.resolve(201);
  1768. return result;
  1769. });
  1770. it('dropping files on same dir breadcrumb does nothing', function() {
  1771. var testDir = '/subdir/two/three with space/four/five';
  1772. var moveStub = sinon.stub(filesClient, 'move').returns($.Deferred().promise());
  1773. fileList.changeDirectory(testDir);
  1774. deferredList.resolve(200, [testRoot].concat(testFiles));
  1775. var $crumb = fileList.breadcrumb.$el.find('.crumb:last');
  1776. // no idea what this is but is required by the handler
  1777. var ui = {
  1778. helper: {
  1779. find: sinon.stub()
  1780. }
  1781. };
  1782. // returns a list of tr that were dragged
  1783. ui.helper.find.returns([
  1784. $('<tr data-file="One.txt" data-dir="' + testDir + '"></tr>'),
  1785. $('<tr data-file="Two.jpg" data-dir="' + testDir + '"></tr>')
  1786. ]);
  1787. // simulate drop event
  1788. fileList._onDropOnBreadCrumb(new $.Event('drop', {target: $crumb}), ui);
  1789. // no extra server request
  1790. expect(moveStub.notCalled).toEqual(true);
  1791. });
  1792. });
  1793. describe('Download Url', function() {
  1794. it('returns correct download URL for single files', function() {
  1795. expect(fileList.getDownloadUrl('some file.txt'))
  1796. .toEqual(OC.getRootPath() + '/remote.php/webdav/subdir/some%20file.txt');
  1797. expect(fileList.getDownloadUrl('some file.txt', '/anotherpath/abc'))
  1798. .toEqual(OC.getRootPath() + '/remote.php/webdav/anotherpath/abc/some%20file.txt');
  1799. $('#dir').val('/');
  1800. expect(fileList.getDownloadUrl('some file.txt'))
  1801. .toEqual(OC.getRootPath() + '/remote.php/webdav/some%20file.txt');
  1802. });
  1803. it('returns correct download URL for multiple files', function() {
  1804. expect(fileList.getDownloadUrl(['a b c.txt', 'd e f.txt']))
  1805. .toEqual(OC.getRootPath() + '/index.php/apps/files/ajax/download.php?dir=%2Fsubdir&files=%5B%22a%20b%20c.txt%22%2C%22d%20e%20f.txt%22%5D');
  1806. });
  1807. it('returns the correct ajax URL', function() {
  1808. expect(fileList.getAjaxUrl('test', {a:1, b:'x y'}))
  1809. .toEqual(OC.getRootPath() + '/index.php/apps/files/ajax/test.php?a=1&b=x%20y');
  1810. });
  1811. });
  1812. describe('File selection', function() {
  1813. beforeEach(function() {
  1814. fileList.setFiles(testFiles);
  1815. });
  1816. it('Selects a file when clicking its checkbox', function() {
  1817. var $tr = fileList.findFileEl('One.txt');
  1818. expect($tr.find('input:checkbox').prop('checked')).toEqual(false);
  1819. $tr.find('td.selection input:checkbox').click();
  1820. expect($tr.find('input:checkbox').prop('checked')).toEqual(true);
  1821. });
  1822. it('Selects a range when clicking on one file then Shift clicking on another one', function() {
  1823. var $tr = fileList.findFileEl('One.txt');
  1824. var $tr2 = fileList.findFileEl('Three.pdf');
  1825. var e;
  1826. $tr.find('td.selection input:checkbox').click();
  1827. e = new $.Event('click');
  1828. e.shiftKey = true;
  1829. $tr2.find('td.filename .name').trigger(e);
  1830. expect($tr.find('input:checkbox').prop('checked')).toEqual(true);
  1831. expect($tr2.find('input:checkbox').prop('checked')).toEqual(true);
  1832. expect(fileList.findFileEl('Two.jpg').find('input:checkbox').prop('checked')).toEqual(true);
  1833. var selection = _.pluck(fileList.getSelectedFiles(), 'name');
  1834. expect(selection.length).toEqual(3);
  1835. expect(selection).toContain('One.txt');
  1836. expect(selection).toContain('Two.jpg');
  1837. expect(selection).toContain('Three.pdf');
  1838. });
  1839. it('Selects a range when clicking on one file then Shift clicking on another one that is above the first one', function() {
  1840. var $tr = fileList.findFileEl('One.txt');
  1841. var $tr2 = fileList.findFileEl('Three.pdf');
  1842. var e;
  1843. $tr2.find('td.selection input:checkbox').click();
  1844. e = new $.Event('click');
  1845. e.shiftKey = true;
  1846. $tr.find('td.filename .name').trigger(e);
  1847. expect($tr.find('input:checkbox').prop('checked')).toEqual(true);
  1848. expect($tr2.find('input:checkbox').prop('checked')).toEqual(true);
  1849. expect(fileList.findFileEl('Two.jpg').find('input:checkbox').prop('checked')).toEqual(true);
  1850. var selection = _.pluck(fileList.getSelectedFiles(), 'name');
  1851. expect(selection.length).toEqual(3);
  1852. expect(selection).toContain('One.txt');
  1853. expect(selection).toContain('Two.jpg');
  1854. expect(selection).toContain('Three.pdf');
  1855. });
  1856. it('Selecting all files will automatically check "select all" checkbox', function() {
  1857. expect($('.select-all').prop('checked')).toEqual(false);
  1858. $('#fileList tr td.selection input:checkbox').click();
  1859. expect($('.select-all').prop('checked')).toEqual(true);
  1860. });
  1861. it('Selecting all files on the first visible page will not automatically check "select all" checkbox', function() {
  1862. fileList.setFiles(generateFiles(0, 41));
  1863. expect($('.select-all').prop('checked')).toEqual(false);
  1864. $('#fileList tr td.selection input:checkbox').click();
  1865. expect($('.select-all').prop('checked')).toEqual(false);
  1866. });
  1867. it('Selecting all files also selects hidden files when invisible', function() {
  1868. filesConfig.set('showhidden', false);
  1869. var $tr = fileList.add(new FileInfo({
  1870. name: '.hidden',
  1871. type: 'dir',
  1872. mimetype: 'httpd/unix-directory',
  1873. size: 150
  1874. }));
  1875. $('.select-all').click();
  1876. expect($tr.find('td.selection input:checkbox').prop('checked')).toEqual(true);
  1877. expect(_.pluck(fileList.getSelectedFiles(), 'name')).toContain('.hidden');
  1878. });
  1879. it('Clicking "select all" will select/deselect all files', function() {
  1880. fileList.setFiles(generateFiles(0, 41));
  1881. $('.select-all').click();
  1882. expect($('.select-all').prop('checked')).toEqual(true);
  1883. $('#fileList tr input:checkbox').each(function() {
  1884. expect($(this).prop('checked')).toEqual(true);
  1885. });
  1886. expect(_.pluck(fileList.getSelectedFiles(), 'name').length).toEqual(42);
  1887. $('.select-all').click();
  1888. expect($('.select-all').prop('checked')).toEqual(false);
  1889. $('#fileList tr input:checkbox').each(function() {
  1890. expect($(this).prop('checked')).toEqual(false);
  1891. });
  1892. expect(_.pluck(fileList.getSelectedFiles(), 'name').length).toEqual(0);
  1893. });
  1894. it('Clicking "select all" then deselecting a file will uncheck "select all"', function() {
  1895. $('.select-all').click();
  1896. expect($('.select-all').prop('checked')).toEqual(true);
  1897. var $tr = fileList.findFileEl('One.txt');
  1898. $tr.find('input:checkbox').click();
  1899. expect($('.select-all').prop('checked')).toEqual(false);
  1900. expect(_.pluck(fileList.getSelectedFiles(), 'name').length).toEqual(3);
  1901. });
  1902. it('Updates the selection summary when doing a few manipulations with "Select all"', function() {
  1903. $('.select-all').click();
  1904. expect($('.select-all').prop('checked')).toEqual(true);
  1905. var $tr = fileList.findFileEl('One.txt');
  1906. // unselect one
  1907. $tr.find('input:checkbox').click();
  1908. expect($('.select-all').prop('checked')).toEqual(false);
  1909. expect(_.pluck(fileList.getSelectedFiles(), 'name').length).toEqual(3);
  1910. // select all
  1911. $('.select-all').click();
  1912. expect($('.select-all').prop('checked')).toEqual(true);
  1913. expect(_.pluck(fileList.getSelectedFiles(), 'name').length).toEqual(4);
  1914. // unselect one
  1915. $tr.find('input:checkbox').click();
  1916. expect($('.select-all').prop('checked')).toEqual(false);
  1917. expect(_.pluck(fileList.getSelectedFiles(), 'name').length).toEqual(3);
  1918. // re-select it
  1919. $tr.find('input:checkbox').click();
  1920. expect($('.select-all').prop('checked')).toEqual(true);
  1921. expect(_.pluck(fileList.getSelectedFiles(), 'name').length).toEqual(4);
  1922. });
  1923. it('Auto-selects files on next page when "select all" is checked', function() {
  1924. fileList.setFiles(generateFiles(0, 41));
  1925. $('.select-all').click();
  1926. expect(fileList.$fileList.find('tr input:checkbox:checked').length).toEqual(20);
  1927. fileList._nextPage(true);
  1928. expect(fileList.$fileList.find('tr input:checkbox:checked').length).toEqual(40);
  1929. fileList._nextPage(true);
  1930. expect(fileList.$fileList.find('tr input:checkbox:checked').length).toEqual(42);
  1931. expect(_.pluck(fileList.getSelectedFiles(), 'name').length).toEqual(42);
  1932. });
  1933. it('Selecting files updates selection summary', function() {
  1934. var $summary = $('#headerName a.name>span:first');
  1935. expect($summary.text()).toEqual('Name');
  1936. fileList.findFileEl('One.txt').find('input:checkbox').click();
  1937. fileList.findFileEl('Three.pdf').find('input:checkbox').click();
  1938. fileList.findFileEl('somedir').find('input:checkbox').click();
  1939. expect($summary.text()).toEqual('1 folder and 2 files');
  1940. });
  1941. it('Unselecting files hides selection summary', function() {
  1942. var $summary = $('#headerName a.name>span:first');
  1943. fileList.findFileEl('One.txt').find('input:checkbox').click().click();
  1944. expect($summary.text()).toEqual('Name');
  1945. });
  1946. it('Displays the number of hidden files in selection summary if hidden files are invisible', function() {
  1947. filesConfig.set('showhidden', false);
  1948. var $tr = fileList.add(new FileInfo({
  1949. name: '.hidden',
  1950. type: 'dir',
  1951. mimetype: 'httpd/unix-directory',
  1952. size: 150
  1953. }));
  1954. $('.select-all').click();
  1955. var $summary = $('#headerName a.name>span:first');
  1956. expect($summary.text()).toEqual('2 folders and 3 files (including 1 hidden)');
  1957. });
  1958. it('Does not displays the number of hidden files in selection summary if hidden files are visible', function() {
  1959. filesConfig.set('showhidden', true);
  1960. var $tr = fileList.add(new FileInfo({
  1961. name: '.hidden',
  1962. type: 'dir',
  1963. mimetype: 'httpd/unix-directory',
  1964. size: 150
  1965. }));
  1966. $('.select-all').click();
  1967. var $summary = $('#headerName a.name>span:first');
  1968. expect($summary.text()).toEqual('2 folders and 3 files');
  1969. });
  1970. it('Toggling hidden file visibility updates selection summary', function() {
  1971. filesConfig.set('showhidden', false);
  1972. var $tr = fileList.add(new FileInfo({
  1973. name: '.hidden',
  1974. type: 'dir',
  1975. mimetype: 'httpd/unix-directory',
  1976. size: 150
  1977. }));
  1978. $('.select-all').click();
  1979. var $summary = $('#headerName a.name>span:first');
  1980. expect($summary.text()).toEqual('2 folders and 3 files (including 1 hidden)');
  1981. filesConfig.set('showhidden', true);
  1982. expect($summary.text()).toEqual('2 folders and 3 files');
  1983. });
  1984. it('Select/deselect files shows/hides file actions', function() {
  1985. var $actions = $('#headerName .selectedActions');
  1986. var $checkbox = fileList.findFileEl('One.txt').find('input:checkbox');
  1987. expect($actions.hasClass('hidden')).toEqual(true);
  1988. $checkbox.click();
  1989. expect($actions.hasClass('hidden')).toEqual(false);
  1990. $checkbox.click();
  1991. expect($actions.hasClass('hidden')).toEqual(true);
  1992. });
  1993. it('Selection is cleared when switching dirs', function() {
  1994. $('.select-all').click();
  1995. var deferredList = $.Deferred();
  1996. var getFolderContentsStub = sinon.stub(filesClient, 'getFolderContents').returns(deferredList.promise());
  1997. fileList.changeDirectory('/');
  1998. deferredList.resolve(200, [testRoot].concat(testFiles));
  1999. expect($('.select-all').prop('checked')).toEqual(false);
  2000. expect(_.pluck(fileList.getSelectedFiles(), 'name')).toEqual([]);
  2001. getFolderContentsStub.restore();
  2002. });
  2003. it('getSelectedFiles returns the selected files even when they are on the next page', function() {
  2004. var selectedFiles;
  2005. fileList.setFiles(generateFiles(0, 41));
  2006. $('.select-all').click();
  2007. // unselect one to not have the "allFiles" case
  2008. fileList.$fileList.find('tr input:checkbox:first').click();
  2009. // only 20 files visible, must still return all the selected ones
  2010. selectedFiles = _.pluck(fileList.getSelectedFiles(), 'name');
  2011. expect(selectedFiles.length).toEqual(41);
  2012. });
  2013. describe('clearing the selection', function() {
  2014. it('clears selected files selected individually calling setFiles()', function() {
  2015. var selectedFiles;
  2016. fileList.setFiles(generateFiles(0, 41));
  2017. fileList.$fileList.find('tr:eq(5) input:checkbox:first').click();
  2018. fileList.$fileList.find('tr:eq(7) input:checkbox:first').click();
  2019. selectedFiles = _.pluck(fileList.getSelectedFiles(), 'name');
  2020. expect(selectedFiles.length).toEqual(2);
  2021. fileList.setFiles(generateFiles(0, 2));
  2022. selectedFiles = _.pluck(fileList.getSelectedFiles(), 'name');
  2023. expect(selectedFiles.length).toEqual(0);
  2024. });
  2025. it('clears selected files selected with select all when calling setFiles()', function() {
  2026. var selectedFiles;
  2027. fileList.setFiles(generateFiles(0, 41));
  2028. $('.select-all').click();
  2029. selectedFiles = _.pluck(fileList.getSelectedFiles(), 'name');
  2030. expect(selectedFiles.length).toEqual(42);
  2031. fileList.setFiles(generateFiles(0, 2));
  2032. selectedFiles = _.pluck(fileList.getSelectedFiles(), 'name');
  2033. expect(selectedFiles.length).toEqual(0);
  2034. });
  2035. });
  2036. describe('Selection overlay', function() {
  2037. it('show doesnt show the copy/move action if one or more files are not copiable/movable', function () {
  2038. fileList.setFiles(testFiles);
  2039. $('#permissions').val(OC.PERMISSION_READ | OC.PERMISSION_UPDATE);
  2040. $('.select-all').click();
  2041. expect(fileList.$el.find('.selectedActions .item-copyMove').hasClass('hidden')).toEqual(false);
  2042. expect(fileList.$el.find('.selectedActions .item-copyMove .label').text()).toEqual('Move or copy');
  2043. testFiles[0].permissions = OC.PERMISSION_READ;
  2044. $('.select-all').click();
  2045. fileList.setFiles(testFiles);
  2046. $('.select-all').click();
  2047. expect(fileList.$el.find('.selectedActions .item-copyMove').hasClass('hidden')).toEqual(false);
  2048. expect(fileList.$el.find('.selectedActions .item-copyMove .label').text()).toEqual('Copy');
  2049. testFiles[0].permissions = OC.PERMISSION_NONE;
  2050. $('.select-all').click();
  2051. fileList.setFiles(testFiles);
  2052. $('.select-all').click();
  2053. expect(fileList.$el.find('.selectedActions .item-copyMove').hasClass('hidden')).toEqual(true);
  2054. });
  2055. it('show doesnt show the download action if one or more files are not downloadable', function () {
  2056. fileList.setFiles(testFiles);
  2057. $('#permissions').val(OC.PERMISSION_READ | OC.PERMISSION_UPDATE);
  2058. $('.select-all').click();
  2059. expect(fileList.$el.find('.selectedActions .item-download').hasClass('hidden')).toEqual(false);
  2060. testFiles[0].permissions = OC.PERMISSION_UPDATE;
  2061. $('.select-all').click();
  2062. fileList.setFiles(testFiles);
  2063. $('.select-all').click();
  2064. expect(fileList.$el.find('.selectedActions .item-download').hasClass('hidden')).toEqual(true);
  2065. });
  2066. it('show doesnt show the delete action if one or more files are not deletable', function () {
  2067. fileList.setFiles(testFiles);
  2068. $('#permissions').val(OC.PERMISSION_READ | OC.PERMISSION_DELETE);
  2069. $('.select-all').click();
  2070. expect(fileList.$el.find('.selectedActions .item-delete').hasClass('hidden')).toEqual(false);
  2071. testFiles[0].permissions = OC.PERMISSION_READ;
  2072. $('.select-all').click();
  2073. fileList.setFiles(testFiles);
  2074. $('.select-all').click();
  2075. expect(fileList.$el.find('.selectedActions .item-delete').hasClass('hidden')).toEqual(true);
  2076. });
  2077. });
  2078. describe('Actions', function() {
  2079. beforeEach(function() {
  2080. fileList.findFileEl('One.txt').find('input:checkbox').click();
  2081. fileList.findFileEl('Three.pdf').find('input:checkbox').click();
  2082. fileList.findFileEl('somedir').find('input:checkbox').click();
  2083. });
  2084. it('getSelectedFiles returns the selected file data', function() {
  2085. var files = fileList.getSelectedFiles();
  2086. expect(files.length).toEqual(3);
  2087. expect(files[0]).toEqual({
  2088. id: 1,
  2089. name: 'One.txt',
  2090. mimetype: 'text/plain',
  2091. mtime: 123456789,
  2092. type: 'file',
  2093. size: 12,
  2094. etag: 'abc',
  2095. quotaAvailableBytes: '-1',
  2096. permissions: OC.PERMISSION_ALL,
  2097. hasPreview: true,
  2098. isEncrypted: false
  2099. });
  2100. expect(files[1]).toEqual({
  2101. id: 3,
  2102. type: 'file',
  2103. name: 'Three.pdf',
  2104. mimetype: 'application/pdf',
  2105. mtime: 234560000,
  2106. size: 58009,
  2107. quotaAvailableBytes: '-1',
  2108. etag: '123',
  2109. permissions: OC.PERMISSION_ALL,
  2110. hasPreview: true,
  2111. isEncrypted: false
  2112. });
  2113. expect(files[2]).toEqual({
  2114. id: 4,
  2115. type: 'dir',
  2116. name: 'somedir',
  2117. mimetype: 'httpd/unix-directory',
  2118. mtime: 134560000,
  2119. size: 250,
  2120. quotaAvailableBytes: '-1',
  2121. etag: '456',
  2122. permissions: OC.PERMISSION_ALL,
  2123. hasPreview: true,
  2124. isEncrypted: false
  2125. });
  2126. expect(files[0].id).toEqual(1);
  2127. expect(files[0].name).toEqual('One.txt');
  2128. expect(files[1].id).toEqual(3);
  2129. expect(files[1].name).toEqual('Three.pdf');
  2130. expect(files[2].id).toEqual(4);
  2131. expect(files[2].name).toEqual('somedir');
  2132. });
  2133. it('Removing a file removes it from the selection', function() {
  2134. fileList.remove('Three.pdf');
  2135. var files = fileList.getSelectedFiles();
  2136. expect(files.length).toEqual(2);
  2137. expect(files[0]).toEqual({
  2138. id: 1,
  2139. name: 'One.txt',
  2140. mimetype: 'text/plain',
  2141. mtime: 123456789,
  2142. type: 'file',
  2143. size: 12,
  2144. quotaAvailableBytes: '-1',
  2145. etag: 'abc',
  2146. permissions: OC.PERMISSION_ALL,
  2147. hasPreview: true,
  2148. isEncrypted: false
  2149. });
  2150. expect(files[1]).toEqual({
  2151. id: 4,
  2152. type: 'dir',
  2153. name: 'somedir',
  2154. mimetype: 'httpd/unix-directory',
  2155. mtime: 134560000,
  2156. size: 250,
  2157. quotaAvailableBytes: '-1',
  2158. etag: '456',
  2159. permissions: OC.PERMISSION_ALL,
  2160. hasPreview: true,
  2161. isEncrypted: false
  2162. });
  2163. });
  2164. describe('Download', function() {
  2165. beforeEach(function() {
  2166. fileList.$el.find('.actions-selected').click();
  2167. });
  2168. it('Opens download URL when clicking "Download"', function() {
  2169. $('.selectedActions .filesSelectMenu .download').click();
  2170. expect(redirectStub.calledOnce).toEqual(true);
  2171. expect(redirectStub.getCall(0).args[0]).toContain(OC.getRootPath() + '/index.php/apps/files/ajax/download.php?dir=%2Fsubdir&files=%5B%22One.txt%22%2C%22Three.pdf%22%2C%22somedir%22%5D');
  2172. redirectStub.restore();
  2173. });
  2174. it('Downloads root folder when all selected in root folder', function() {
  2175. $('#dir').val('/');
  2176. $('.select-all').click();
  2177. $('.selectedActions .filesSelectMenu .download').click();
  2178. expect(redirectStub.calledOnce).toEqual(true);
  2179. expect(redirectStub.getCall(0).args[0]).toContain(OC.getRootPath() + '/index.php/apps/files/ajax/download.php?dir=%2F&files=');
  2180. });
  2181. it('Downloads parent folder when all selected in subfolder', function() {
  2182. $('.select-all').click();
  2183. $('.selectedActions .filesSelectMenu .download').click();
  2184. expect(redirectStub.calledOnce).toEqual(true);
  2185. expect(redirectStub.getCall(0).args[0]).toContain(OC.getRootPath() + '/index.php/apps/files/ajax/download.php?dir=%2F&files=subdir');
  2186. });
  2187. afterEach(function() {
  2188. fileList.$el.find('.actions-selected').click();
  2189. });
  2190. });
  2191. describe('Delete', function() {
  2192. var deleteStub, deferredDelete;
  2193. beforeEach(function() {
  2194. deferredDelete = $.Deferred();
  2195. deleteStub = sinon.stub(filesClient, 'remove');
  2196. fileList.$el.find('.actions-selected').click();
  2197. });
  2198. afterEach(function() {
  2199. fileList.$el.find('.actions-selected').click();
  2200. deleteStub.restore();
  2201. });
  2202. it('Deletes selected files when "Delete" clicked', function(done) {
  2203. var deferred = $.Deferred();
  2204. deleteStub.returns(deferredDelete.promise());
  2205. deleteStub.onCall(2).callsFake(function(){
  2206. expect(deleteStub.callCount).toEqual(3);
  2207. expect(deleteStub.getCall(0).args[0]).toEqual('/subdir/One.txt');
  2208. expect(deleteStub.getCall(1).args[0]).toEqual('/subdir/Three.pdf');
  2209. expect(deleteStub.getCall(2).args[0]).toEqual('/subdir/somedir');
  2210. return deferredDelete.promise();
  2211. });
  2212. stub = sinon.stub(fileList._operationProgressBar, 'hideProgressBar').callsFake(function(){
  2213. expect(fileList.findFileEl('One.txt').length).toEqual(0);
  2214. expect(fileList.findFileEl('Three.pdf').length).toEqual(0);
  2215. expect(fileList.findFileEl('somedir').length).toEqual(0);
  2216. expect(fileList.findFileEl('Two.jpg').length).toEqual(1);
  2217. done();
  2218. deferred.resolve();
  2219. });
  2220. $('.selectedActions .filesSelectMenu .delete').click();
  2221. deferredDelete.resolve(204);
  2222. return deferred.promise();
  2223. });
  2224. it('Deletes all files when all selected when "Delete" clicked', function(done) {
  2225. var deferred = $.Deferred();
  2226. deleteStub.returns(deferredDelete.promise());
  2227. deleteStub.onCall(3).callsFake(function(){
  2228. expect(deleteStub.getCall(0).args[0]).toEqual('/subdir/One.txt');
  2229. expect(deleteStub.getCall(1).args[0]).toEqual('/subdir/Two.jpg');
  2230. expect(deleteStub.getCall(2).args[0]).toEqual('/subdir/Three.pdf');
  2231. expect(deleteStub.getCall(3).args[0]).toEqual('/subdir/somedir');
  2232. return deferredDelete.promise();
  2233. });
  2234. stub = sinon.stub(fileList._operationProgressBar, 'hideProgressBar').callsFake(function(){
  2235. expect(fileList.isEmpty).toEqual(true);
  2236. expect(deleteStub.callCount).toEqual(4);
  2237. done();
  2238. deferred.resolve();
  2239. });
  2240. $('.select-all').click();
  2241. $('.selectedActions .filesSelectMenu .delete').click();
  2242. deferredDelete.resolve(204);
  2243. return deferred.promise();
  2244. });
  2245. });
  2246. });
  2247. it('resets the file selection on reload', function() {
  2248. fileList.$el.find('.select-all').click();
  2249. fileList.reload();
  2250. expect(fileList.$el.find('.select-all').prop('checked')).toEqual(false);
  2251. expect(fileList.getSelectedFiles()).toEqual([]);
  2252. });
  2253. describe('Disabled selection', function() {
  2254. beforeEach(function() {
  2255. fileList._allowSelection = false;
  2256. fileList.setFiles(testFiles);
  2257. });
  2258. it('Does not render checkboxes', function() {
  2259. expect(fileList.$fileList.find('.selectCheckBox').length).toEqual(0);
  2260. });
  2261. it('Does not select a file with Ctrl or Shift if selection is not allowed', function() {
  2262. var $tr = fileList.findFileEl('One.txt');
  2263. var $tr2 = fileList.findFileEl('Three.pdf');
  2264. var e;
  2265. e = new $.Event('click');
  2266. e.ctrlKey = true;
  2267. $tr.find('td.filename .name').trigger(e);
  2268. // click on second entry, does not clear the selection
  2269. e = new $.Event('click');
  2270. e.ctrlKey = true;
  2271. $tr2.find('td.filename .name').trigger(e);
  2272. expect(fileList.getSelectedFiles().length).toEqual(0);
  2273. // deselect now
  2274. e = new $.Event('click');
  2275. e.shiftKey = true;
  2276. $tr2.find('td.filename .name').trigger(e);
  2277. expect(fileList.getSelectedFiles().length).toEqual(0);
  2278. });
  2279. });
  2280. });
  2281. describe('File actions', function() {
  2282. it('Clicking on a file name will trigger default action', function() {
  2283. var actionStub = sinon.stub();
  2284. fileList.setFiles(testFiles);
  2285. fileList.fileActions.registerAction({
  2286. mime: 'text/plain',
  2287. name: 'Test',
  2288. type: OCA.Files.FileActions.TYPE_INLINE,
  2289. permissions: OC.PERMISSION_ALL,
  2290. icon: function() {
  2291. // Specify icon for hitory button
  2292. return OC.imagePath('core','actions/history');
  2293. },
  2294. actionHandler: actionStub
  2295. });
  2296. fileList.fileActions.setDefault('text/plain', 'Test');
  2297. var $tr = fileList.findFileEl('One.txt');
  2298. $tr.find('td.filename .nametext').click();
  2299. expect(actionStub.calledOnce).toEqual(true);
  2300. expect(actionStub.getCall(0).args[0]).toEqual('One.txt');
  2301. var context = actionStub.getCall(0).args[1];
  2302. expect(context.$file.is($tr)).toEqual(true);
  2303. expect(context.fileList).toBeDefined();
  2304. expect(context.fileActions).toBeDefined();
  2305. expect(context.dir).toEqual('/subdir');
  2306. });
  2307. it('Clicking on an empty space of the file row will trigger the "Details" action', function() {
  2308. var detailsActionStub = sinon.stub();
  2309. fileList.setFiles(testFiles);
  2310. // Override the "Details" action set internally by the FileList for
  2311. // easier testing.
  2312. fileList.fileActions.registerAction({
  2313. mime: 'all',
  2314. name: 'Details',
  2315. permissions: OC.PERMISSION_NONE,
  2316. actionHandler: detailsActionStub
  2317. });
  2318. // Ensure that the action works even if fileActions.currentFile is
  2319. // not set.
  2320. fileList.fileActions.currentFile = null;
  2321. var $tr = fileList.findFileEl('One.txt');
  2322. $tr.find('td.filesize').click();
  2323. expect(detailsActionStub.calledOnce).toEqual(true);
  2324. expect(detailsActionStub.getCall(0).args[0]).toEqual('One.txt');
  2325. var context = detailsActionStub.getCall(0).args[1];
  2326. expect(context.$file.is($tr)).toEqual(true);
  2327. expect(context.fileList).toBe(fileList);
  2328. expect(context.fileActions).toBe(fileList.fileActions);
  2329. expect(context.dir).toEqual('/subdir');
  2330. });
  2331. it('redisplays actions when new actions have been registered', function() {
  2332. var actionStub = sinon.stub();
  2333. var readyHandler = sinon.stub();
  2334. var clock = sinon.useFakeTimers();
  2335. var debounceStub = sinon.stub(_, 'debounce').callsFake(function(callback) {
  2336. return function() {
  2337. // defer instead of debounce, to make it work with clock
  2338. _.defer(callback);
  2339. };
  2340. });
  2341. // need to reinit the list to make the debounce call
  2342. fileList.destroy();
  2343. fileList = new OCA.Files.FileList($('#app-content-files'));
  2344. fileList.setFiles(testFiles);
  2345. fileList.$fileList.on('fileActionsReady', readyHandler);
  2346. fileList.fileActions.registerAction({
  2347. mime: 'text/plain',
  2348. name: 'Test',
  2349. type: OCA.Files.FileActions.TYPE_INLINE,
  2350. permissions: OC.PERMISSION_ALL,
  2351. icon: function() {
  2352. // Specify icon for hitory button
  2353. return OC.imagePath('core','actions/history');
  2354. },
  2355. actionHandler: actionStub
  2356. });
  2357. var $tr = fileList.findFileEl('One.txt');
  2358. expect($tr.find('.action-test').length).toEqual(0);
  2359. expect(readyHandler.notCalled).toEqual(true);
  2360. // update is delayed
  2361. clock.tick(100);
  2362. expect($tr.find('.action-test').length).toEqual(1);
  2363. expect(readyHandler.calledOnce).toEqual(true);
  2364. clock.restore();
  2365. debounceStub.restore();
  2366. });
  2367. });
  2368. describe('Sorting files', function() {
  2369. var getCurrentUserStub;
  2370. beforeEach(function() {
  2371. getCurrentUserStub = sinon.stub(OC, 'getCurrentUser').returns({
  2372. uid: 1,
  2373. displayName: 'user1'
  2374. });
  2375. });
  2376. afterEach(function() {
  2377. getCurrentUserStub.restore();
  2378. });
  2379. it('Toggles the sort indicator when clicking on a column header', function() {
  2380. var ASC_CLASS = fileList.SORT_INDICATOR_ASC_CLASS;
  2381. var DESC_CLASS = fileList.SORT_INDICATOR_DESC_CLASS;
  2382. var request;
  2383. var sortingUrl = OC.generateUrl('/apps/files/api/v1/sorting');
  2384. fileList.$el.find('.column-size .columntitle').click();
  2385. // moves triangle to size column, check indicator on name is hidden
  2386. expect(
  2387. fileList.$el.find('.column-name .sort-indicator').hasClass('hidden')
  2388. ).toEqual(true);
  2389. // check indicator on size is visible and defaults to descending
  2390. expect(
  2391. fileList.$el.find('.column-size .sort-indicator').hasClass('hidden')
  2392. ).toEqual(false);
  2393. expect(
  2394. fileList.$el.find('.column-size .sort-indicator').hasClass(DESC_CLASS)
  2395. ).toEqual(true);
  2396. // check if changes are persisted
  2397. expect(fakeServer.requests.length).toEqual(1);
  2398. request = fakeServer.requests[0];
  2399. expect(request.url).toEqual(sortingUrl);
  2400. // click again on size column, reverses direction
  2401. fileList.$el.find('.column-size .columntitle').click();
  2402. expect(
  2403. fileList.$el.find('.column-size .sort-indicator').hasClass('hidden')
  2404. ).toEqual(false);
  2405. expect(
  2406. fileList.$el.find('.column-size .sort-indicator').hasClass(ASC_CLASS)
  2407. ).toEqual(true);
  2408. // check if changes are persisted
  2409. expect(fakeServer.requests.length).toEqual(2);
  2410. request = fakeServer.requests[1];
  2411. expect(request.url).toEqual(sortingUrl);
  2412. // click again on size column, reverses direction
  2413. fileList.$el.find('.column-size .columntitle').click();
  2414. expect(
  2415. fileList.$el.find('.column-size .sort-indicator').hasClass('hidden')
  2416. ).toEqual(false);
  2417. expect(
  2418. fileList.$el.find('.column-size .sort-indicator').hasClass(DESC_CLASS)
  2419. ).toEqual(true);
  2420. expect(fakeServer.requests.length).toEqual(3);
  2421. request = fakeServer.requests[2];
  2422. expect(request.url).toEqual(sortingUrl);
  2423. // click on mtime column, moves indicator there
  2424. fileList.$el.find('.column-mtime .columntitle').click();
  2425. expect(
  2426. fileList.$el.find('.column-size .sort-indicator').hasClass('hidden')
  2427. ).toEqual(true);
  2428. expect(
  2429. fileList.$el.find('.column-mtime .sort-indicator').hasClass('hidden')
  2430. ).toEqual(false);
  2431. expect(
  2432. fileList.$el.find('.column-mtime .sort-indicator').hasClass(DESC_CLASS)
  2433. ).toEqual(true);
  2434. expect(fakeServer.requests.length).toEqual(4);
  2435. request = fakeServer.requests[3];
  2436. expect(request.url).toEqual(sortingUrl);
  2437. });
  2438. it('Uses correct sort comparator when inserting files', function() {
  2439. testFiles.sort(OCA.Files.FileList.Comparators.size);
  2440. testFiles.reverse(); //default is descending
  2441. fileList.setFiles(testFiles);
  2442. fileList.$el.find('.column-size .columntitle').click();
  2443. var newFileData = new FileInfo({
  2444. id: 999,
  2445. name: 'new file.txt',
  2446. mimetype: 'text/plain',
  2447. size: 40001,
  2448. etag: '999'
  2449. });
  2450. fileList.add(newFileData);
  2451. expect(fileList.findFileEl('Three.pdf').index()).toEqual(0);
  2452. expect(fileList.findFileEl('new file.txt').index()).toEqual(1);
  2453. expect(fileList.findFileEl('Two.jpg').index()).toEqual(2);
  2454. expect(fileList.findFileEl('somedir').index()).toEqual(3);
  2455. expect(fileList.findFileEl('One.txt').index()).toEqual(4);
  2456. expect(fileList.files.length).toEqual(5);
  2457. expect(fileList.$fileList.find('tr').length).toEqual(5);
  2458. });
  2459. it('Uses correct reversed sort comparator when inserting files', function() {
  2460. testFiles.sort(OCA.Files.FileList.Comparators.size);
  2461. fileList.setFiles(testFiles);
  2462. fileList.$el.find('.column-size .columntitle').click();
  2463. // reverse sort
  2464. fileList.$el.find('.column-size .columntitle').click();
  2465. var newFileData = new FileInfo({
  2466. id: 999,
  2467. name: 'new file.txt',
  2468. mimetype: 'text/plain',
  2469. size: 40001,
  2470. etag: '999'
  2471. });
  2472. fileList.add(newFileData);
  2473. expect(fileList.findFileEl('One.txt').index()).toEqual(0);
  2474. expect(fileList.findFileEl('somedir').index()).toEqual(1);
  2475. expect(fileList.findFileEl('Two.jpg').index()).toEqual(2);
  2476. expect(fileList.findFileEl('new file.txt').index()).toEqual(3);
  2477. expect(fileList.findFileEl('Three.pdf').index()).toEqual(4);
  2478. expect(fileList.files.length).toEqual(5);
  2479. expect(fileList.$fileList.find('tr').length).toEqual(5);
  2480. });
  2481. it('does not sort when clicking on header whenever multiselect is enabled', function() {
  2482. var sortStub = sinon.stub(OCA.Files.FileList.prototype, 'setSort');
  2483. fileList.setFiles(testFiles);
  2484. fileList.findFileEl('One.txt').find('input:checkbox:first').click();
  2485. fileList.$el.find('.column-size .columntitle').click();
  2486. expect(sortStub.notCalled).toEqual(true);
  2487. // can sort again after deselecting
  2488. fileList.findFileEl('One.txt').find('input:checkbox:first').click();
  2489. fileList.$el.find('.column-size .columntitle').click();
  2490. expect(sortStub.calledOnce).toEqual(true);
  2491. sortStub.restore();
  2492. });
  2493. describe('if no user logged in', function() {
  2494. beforeEach(function() {
  2495. getCurrentUserStub.returns({
  2496. uid: null,
  2497. displayName: 'Guest'
  2498. });
  2499. });
  2500. it('shouldn\'t send an update sort order request', function() {
  2501. OC.currentUser = false;
  2502. fileList.$el.find('.column-size .columntitle').click();
  2503. // check if there was no request
  2504. expect(fakeServer.requests.length).toEqual(0);
  2505. });
  2506. });
  2507. describe('with favorites', function() {
  2508. it('shows favorite files on top', function() {
  2509. testFiles.push(new FileInfo({
  2510. id: 5,
  2511. type: 'file',
  2512. name: 'ZZY Before last file in ascending order',
  2513. mimetype: 'text/plain',
  2514. mtime: 999999998,
  2515. size: 9999998,
  2516. // Tags would be added by TagsPlugin
  2517. tags: [OC.TAG_FAVORITE],
  2518. }), new FileInfo({
  2519. id: 6,
  2520. type: 'file',
  2521. name: 'ZZZ Last file in ascending order',
  2522. mimetype: 'text/plain',
  2523. mtime: 999999999,
  2524. size: 9999999,
  2525. // Tags would be added by TagsPlugin
  2526. tags: [OC.TAG_FAVORITE],
  2527. }));
  2528. fileList.setFiles(testFiles);
  2529. // Sort by name in ascending order (default sorting is by name
  2530. // in ascending order, but setFiles does not trigger a sort, so
  2531. // the files must be sorted before being set or a sort must be
  2532. // triggered afterwards by clicking on the header).
  2533. fileList.$el.find('.column-name .columntitle').click();
  2534. fileList.$el.find('.column-name .columntitle').click();
  2535. expect(fileList.findFileEl('ZZY Before last file in ascending order').index()).toEqual(0);
  2536. expect(fileList.findFileEl('ZZZ Last file in ascending order').index()).toEqual(1);
  2537. expect(fileList.findFileEl('somedir').index()).toEqual(2);
  2538. expect(fileList.findFileEl('One.txt').index()).toEqual(3);
  2539. expect(fileList.findFileEl('Three.pdf').index()).toEqual(4);
  2540. expect(fileList.findFileEl('Two.jpg').index()).toEqual(5);
  2541. // Sort by size in ascending order
  2542. fileList.$el.find('.column-size .columntitle').click();
  2543. fileList.$el.find('.column-size .columntitle').click();
  2544. expect(fileList.findFileEl('ZZY Before last file in ascending order').index()).toEqual(0);
  2545. expect(fileList.findFileEl('ZZZ Last file in ascending order').index()).toEqual(1);
  2546. expect(fileList.findFileEl('One.txt').index()).toEqual(2);
  2547. expect(fileList.findFileEl('somedir').index()).toEqual(3);
  2548. expect(fileList.findFileEl('Two.jpg').index()).toEqual(4);
  2549. expect(fileList.findFileEl('Three.pdf').index()).toEqual(5);
  2550. // Sort by modification time in ascending order
  2551. fileList.$el.find('.column-mtime .columntitle').click();
  2552. fileList.$el.find('.column-mtime .columntitle').click();
  2553. expect(fileList.findFileEl('ZZY Before last file in ascending order').index()).toEqual(0);
  2554. expect(fileList.findFileEl('ZZZ Last file in ascending order').index()).toEqual(1);
  2555. expect(fileList.findFileEl('One.txt').index()).toEqual(2);
  2556. expect(fileList.findFileEl('somedir').index()).toEqual(3);
  2557. expect(fileList.findFileEl('Three.pdf').index()).toEqual(4);
  2558. expect(fileList.findFileEl('Two.jpg').index()).toEqual(5);
  2559. });
  2560. it('shows favorite files on top also when using descending order', function() {
  2561. testFiles.push(new FileInfo({
  2562. id: 5,
  2563. type: 'file',
  2564. name: 'AAB Before last file in descending order',
  2565. mimetype: 'text/plain',
  2566. mtime: 2,
  2567. size: 2,
  2568. // Tags would be added by TagsPlugin
  2569. tags: [OC.TAG_FAVORITE],
  2570. }), new FileInfo({
  2571. id: 6,
  2572. type: 'file',
  2573. name: 'AAA Last file in descending order',
  2574. mimetype: 'text/plain',
  2575. mtime: 1,
  2576. size: 1,
  2577. // Tags would be added by TagsPlugin
  2578. tags: [OC.TAG_FAVORITE],
  2579. }));
  2580. fileList.setFiles(testFiles);
  2581. // Sort by name in descending order
  2582. fileList.$el.find('.column-name .columntitle').click();
  2583. expect(fileList.findFileEl('AAB Before last file in descending order').index()).toEqual(0);
  2584. expect(fileList.findFileEl('AAA Last file in descending order').index()).toEqual(1);
  2585. expect(fileList.findFileEl('Two.jpg').index()).toEqual(2);
  2586. expect(fileList.findFileEl('Three.pdf').index()).toEqual(3);
  2587. expect(fileList.findFileEl('One.txt').index()).toEqual(4);
  2588. expect(fileList.findFileEl('somedir').index()).toEqual(5);
  2589. // Sort by size in descending order
  2590. fileList.$el.find('.column-size .columntitle').click();
  2591. expect(fileList.findFileEl('AAB Before last file in descending order').index()).toEqual(0);
  2592. expect(fileList.findFileEl('AAA Last file in descending order').index()).toEqual(1);
  2593. expect(fileList.findFileEl('Three.pdf').index()).toEqual(2);
  2594. expect(fileList.findFileEl('Two.jpg').index()).toEqual(3);
  2595. expect(fileList.findFileEl('somedir').index()).toEqual(4);
  2596. expect(fileList.findFileEl('One.txt').index()).toEqual(5);
  2597. // Sort by modification time in descending order
  2598. fileList.$el.find('.column-mtime .columntitle').click();
  2599. expect(fileList.findFileEl('AAB Before last file in descending order').index()).toEqual(0);
  2600. expect(fileList.findFileEl('AAA Last file in descending order').index()).toEqual(1);
  2601. expect(fileList.findFileEl('Two.jpg').index()).toEqual(2);
  2602. expect(fileList.findFileEl('Three.pdf').index()).toEqual(3);
  2603. expect(fileList.findFileEl('somedir').index()).toEqual(4);
  2604. expect(fileList.findFileEl('One.txt').index()).toEqual(5);
  2605. });
  2606. });
  2607. });
  2608. describe('create file', function() {
  2609. var deferredCreate;
  2610. var deferredInfo;
  2611. var createStub;
  2612. var getFileInfoStub;
  2613. beforeEach(function() {
  2614. deferredCreate = $.Deferred();
  2615. deferredInfo = $.Deferred();
  2616. createStub = sinon.stub(filesClient, 'putFileContents')
  2617. .returns(deferredCreate.promise());
  2618. getFileInfoStub = sinon.stub(filesClient, 'getFileInfo')
  2619. .returns(deferredInfo.promise());
  2620. });
  2621. afterEach(function() {
  2622. createStub.restore();
  2623. getFileInfoStub.restore();
  2624. });
  2625. it('creates file with given name and adds it to the list', function(done) {
  2626. var creating = fileList.createFile('test.txt');
  2627. expect(createStub.calledOnce).toEqual(true);
  2628. expect(createStub.getCall(0).args[0]).toEqual('/subdir/test.txt');
  2629. expect(createStub.getCall(0).args[2]).toEqual({
  2630. contentType: 'text/plain',
  2631. overwrite: true
  2632. });
  2633. deferredCreate.resolve(200);
  2634. expect(getFileInfoStub.calledOnce).toEqual(true);
  2635. expect(getFileInfoStub.getCall(0).args[0]).toEqual('/subdir/test.txt');
  2636. deferredInfo.resolve(
  2637. 200,
  2638. new FileInfo({
  2639. path: '/subdir',
  2640. name: 'test.txt',
  2641. mimetype: 'text/plain'
  2642. })
  2643. );
  2644. return creating.then(function() {
  2645. var $tr = fileList.findFileEl('test.txt');
  2646. expect($tr.length).toEqual(1);
  2647. expect($tr.attr('data-mime')).toEqual('text/plain');
  2648. }).then(done, done);
  2649. });
  2650. // TODO: error cases
  2651. // TODO: unique name cases
  2652. });
  2653. describe('create folder', function() {
  2654. var deferredCreate;
  2655. var deferredInfo;
  2656. var createStub;
  2657. var getFileInfoStub;
  2658. beforeEach(function() {
  2659. deferredCreate = $.Deferred();
  2660. deferredInfo = $.Deferred();
  2661. createStub = sinon.stub(filesClient, 'createDirectory')
  2662. .returns(deferredCreate.promise());
  2663. getFileInfoStub = sinon.stub(filesClient, 'getFileInfo')
  2664. .returns(deferredInfo.promise());
  2665. });
  2666. afterEach(function() {
  2667. createStub.restore();
  2668. getFileInfoStub.restore();
  2669. });
  2670. it('creates folder with given name and adds it to the list', function(done) {
  2671. var creating = fileList.createDirectory('sub dir');
  2672. expect(createStub.calledOnce).toEqual(true);
  2673. expect(createStub.getCall(0).args[0]).toEqual('/subdir/sub dir');
  2674. deferredCreate.resolve(200);
  2675. expect(getFileInfoStub.calledOnce).toEqual(true);
  2676. expect(getFileInfoStub.getCall(0).args[0]).toEqual('/subdir/sub dir');
  2677. deferredInfo.resolve(
  2678. 200,
  2679. new FileInfo({
  2680. path: '/subdir',
  2681. name: 'sub dir',
  2682. mimetype: 'httpd/unix-directory'
  2683. })
  2684. );
  2685. return creating.then(function() {
  2686. var $tr = fileList.findFileEl('sub dir');
  2687. expect($tr.length).toEqual(1);
  2688. expect($tr.attr('data-mime')).toEqual('httpd/unix-directory');
  2689. }).then(done, done);
  2690. });
  2691. // TODO: error cases
  2692. // TODO: unique name cases
  2693. });
  2694. describe('addAndFetchFileInfo', function() {
  2695. var getFileInfoStub;
  2696. var getFileInfoDeferred;
  2697. beforeEach(function() {
  2698. getFileInfoDeferred = $.Deferred();
  2699. getFileInfoStub = sinon.stub(OC.Files.Client.prototype, 'getFileInfo');
  2700. getFileInfoStub.returns(getFileInfoDeferred.promise());
  2701. });
  2702. afterEach(function() {
  2703. getFileInfoStub.restore();
  2704. });
  2705. it('does not fetch if the given folder is not the current one', function() {
  2706. var promise = fileList.addAndFetchFileInfo('testfile.txt', '/another');
  2707. expect(getFileInfoStub.notCalled).toEqual(true);
  2708. expect(promise.state()).toEqual('resolved');
  2709. });
  2710. it('fetches info when folder is the current one', function(done) {
  2711. fileList.addAndFetchFileInfo('testfile.txt', '/subdir');
  2712. return Promise.resolve().then(function() {
  2713. expect(getFileInfoStub.calledOnce).toEqual(true);
  2714. expect(getFileInfoStub.getCall(0).args[0]).toEqual('/subdir/testfile.txt');
  2715. }).then(done, done);
  2716. });
  2717. it('adds file data to list when fetching is done', function(done) {
  2718. var adding = fileList.addAndFetchFileInfo('testfile.txt', '/subdir');
  2719. getFileInfoDeferred.resolve(200, {
  2720. name: 'testfile.txt',
  2721. size: 100
  2722. });
  2723. return adding.then(function() {
  2724. expect(fileList.findFileEl('testfile.txt').attr('data-size')).toEqual('100');
  2725. }).then(done, done);
  2726. });
  2727. it('replaces file data to list when fetching is done', function(done) {
  2728. var adding = fileList.addAndFetchFileInfo('testfile.txt', '/subdir', {replace: true});
  2729. fileList.add({
  2730. name: 'testfile.txt',
  2731. size: 95
  2732. });
  2733. getFileInfoDeferred.resolve(200, {
  2734. name: 'testfile.txt',
  2735. size: 100
  2736. });
  2737. expect(fileList.findFileEl('testfile.txt').attr('data-size')).toEqual('95');
  2738. return adding.then(function() {
  2739. expect(fileList.findFileEl('testfile.txt').attr('data-size')).toEqual('100');
  2740. }).then(done, done);
  2741. });
  2742. it('resolves promise with file data when fetching is done', function(done) {
  2743. var promise = fileList.addAndFetchFileInfo('testfile.txt', '/subdir', {replace: true});
  2744. getFileInfoDeferred.resolve(200, {
  2745. name: 'testfile.txt',
  2746. size: 100
  2747. });
  2748. expect(promise.state()).toEqual('pending');
  2749. return promise.then(function(status, data) {
  2750. expect(promise.state()).toEqual('resolved');
  2751. expect(status).toEqual(200);
  2752. expect(data.name).toEqual('testfile.txt');
  2753. expect(data.size).toEqual(100);
  2754. }).then(done, done);
  2755. });
  2756. });
  2757. /**
  2758. * Test upload mostly by testing the code inside the event handlers
  2759. * that were registered on the magic upload object
  2760. */
  2761. describe('file upload', function() {
  2762. var uploadData;
  2763. var uploader;
  2764. beforeEach(function() {
  2765. fileList.setFiles(testFiles);
  2766. uploader = fileList._uploader;
  2767. // simulate data structure from jquery.upload
  2768. uploadData = {
  2769. files: [{
  2770. name: 'upload.txt'
  2771. }]
  2772. };
  2773. });
  2774. afterEach(function() {
  2775. uploader = null;
  2776. uploadData = null;
  2777. });
  2778. describe('enableupload', function() {
  2779. it('sets up uploader when enableUpload is true', function() {
  2780. expect(fileList._uploader).toBeDefined();
  2781. });
  2782. it('does not sets up uploader when enableUpload is false', function() {
  2783. fileList.destroy();
  2784. fileList = new OCA.Files.FileList($('#app-content-files'), {
  2785. filesClient: filesClient
  2786. });
  2787. expect(fileList._uploader).toBeFalsy();
  2788. });
  2789. });
  2790. describe('adding files for upload', function() {
  2791. /**
  2792. * Simulate add event on the given target
  2793. *
  2794. * @return event object including the result
  2795. */
  2796. function addFile(data) {
  2797. uploader.trigger('add', {}, data || {});
  2798. }
  2799. it('sets target dir to the current directory', function() {
  2800. addFile(uploadData);
  2801. expect(uploadData.targetDir).toEqual('/subdir');
  2802. });
  2803. });
  2804. describe('dropping external files', function() {
  2805. /**
  2806. * Simulate drop event on the given target
  2807. *
  2808. * @param $target target element to drop on
  2809. * @return event object including the result
  2810. */
  2811. function dropOn($target, data) {
  2812. var eventData = {
  2813. delegatedEvent: {
  2814. target: $target
  2815. },
  2816. preventDefault: function () {
  2817. },
  2818. stopPropagation: function() {
  2819. }
  2820. };
  2821. uploader.trigger('drop', eventData, data || {});
  2822. return !!data.targetDir;
  2823. }
  2824. it('drop on a tr or crumb outside file list does not trigger upload', function() {
  2825. var $anotherTable = $('<table><tbody><tr><td>outside<div class="crumb">crumb</div></td></tr></table>');
  2826. var ev;
  2827. $('#testArea').append($anotherTable);
  2828. ev = dropOn($anotherTable.find('tr'), uploadData);
  2829. expect(ev).toEqual(false);
  2830. ev = dropOn($anotherTable.find('.crumb'), uploadData);
  2831. expect(ev).toEqual(false);
  2832. });
  2833. it('drop on an element outside file list container does not trigger upload', function() {
  2834. var $anotherEl = $('<div>outside</div>');
  2835. var ev;
  2836. $('#testArea').append($anotherEl);
  2837. ev = dropOn($anotherEl, uploadData);
  2838. expect(ev).toEqual(false);
  2839. });
  2840. it('drop on an element inside the table triggers upload', function() {
  2841. var ev;
  2842. ev = dropOn(fileList.$fileList.find('th:first'), uploadData);
  2843. expect(ev).not.toEqual(false);
  2844. expect(uploadData.targetDir).toEqual('/subdir');
  2845. });
  2846. it('drop on an element on the table container triggers upload', function() {
  2847. var ev;
  2848. ev = dropOn($('#app-content-files'), uploadData);
  2849. expect(ev).not.toEqual(false);
  2850. expect(uploadData.targetDir).toEqual('/subdir');
  2851. });
  2852. it('drop on an element inside the table does not trigger upload if no upload permission', function() {
  2853. $('#permissions').val(0);
  2854. var ev;
  2855. ev = dropOn(fileList.$fileList.find('th:first'), uploadData);
  2856. expect(ev).toEqual(false);
  2857. expect(notificationStub.calledOnce).toEqual(true);
  2858. });
  2859. it('drop on an folder does not trigger upload if no upload permission on that folder', function() {
  2860. var $tr = fileList.findFileEl('somedir');
  2861. var ev;
  2862. $tr.data('permissions', OC.PERMISSION_READ);
  2863. ev = dropOn($tr, uploadData);
  2864. expect(ev).toEqual(false);
  2865. expect(notificationStub.calledOnce).toEqual(true);
  2866. });
  2867. it('drop on a file row inside the table triggers upload to current folder', function() {
  2868. var ev;
  2869. ev = dropOn(fileList.findFileEl('One.txt').find('td:first'), uploadData);
  2870. expect(ev).not.toEqual(false);
  2871. expect(uploadData.targetDir).toEqual('/subdir');
  2872. });
  2873. it('drop on a folder row inside the table triggers upload to target folder', function() {
  2874. var ev;
  2875. ev = dropOn(fileList.findFileEl('somedir').find('td:eq(2)'), uploadData);
  2876. expect(ev).not.toEqual(false);
  2877. expect(uploadData.targetDir).toEqual('/subdir/somedir');
  2878. });
  2879. it('drop on a breadcrumb inside the table triggers upload to target folder', function() {
  2880. var ev;
  2881. fileList.changeDirectory('a/b/c/d');
  2882. ev = dropOn(fileList.$el.find('.crumb:eq(3)'), uploadData);
  2883. expect(ev).not.toEqual(false);
  2884. expect(uploadData.targetDir).toEqual('/a/b');
  2885. });
  2886. it('renders upload indicator element for folders only', function() {
  2887. fileList.add({
  2888. name: 'afolder',
  2889. type: 'dir',
  2890. mime: 'httpd/unix-directory'
  2891. });
  2892. fileList.add({
  2893. name: 'afile.txt',
  2894. type: 'file',
  2895. mime: 'text/plain'
  2896. });
  2897. expect(fileList.findFileEl('afolder').find('.uploadtext').length).toEqual(1);
  2898. expect(fileList.findFileEl('afile.txt').find('.uploadtext').length).toEqual(0);
  2899. });
  2900. });
  2901. describe('after folder creation due to folder upload', function() {
  2902. it('fetches folder info', function() {
  2903. var fetchInfoStub = sinon.stub(fileList, 'addAndFetchFileInfo');
  2904. uploader.trigger('createdfolder', '/subdir/newfolder');
  2905. expect(fetchInfoStub.calledOnce).toEqual(true);
  2906. expect(fetchInfoStub.getCall(0).args[0]).toEqual('newfolder');
  2907. expect(fetchInfoStub.getCall(0).args[1]).toEqual('/subdir');
  2908. fetchInfoStub.restore();
  2909. });
  2910. });
  2911. describe('after upload', function() {
  2912. var fetchInfoStub;
  2913. beforeEach(function() {
  2914. fetchInfoStub = sinon.stub(fileList, 'addAndFetchFileInfo');
  2915. });
  2916. afterEach(function() {
  2917. fetchInfoStub.restore();
  2918. });
  2919. function createUpload(name, dir) {
  2920. var jqXHR = {
  2921. status: 200
  2922. };
  2923. return {
  2924. getFileName: sinon.stub().returns(name),
  2925. getFullPath: sinon.stub().returns(dir),
  2926. data: {
  2927. jqXHR: jqXHR
  2928. }
  2929. };
  2930. }
  2931. /**
  2932. * Simulate add event on the given target
  2933. *
  2934. * @return event object including the result
  2935. */
  2936. function addFile(data) {
  2937. var ev = new $.Event('done', {
  2938. jqXHR: {status: 200}
  2939. });
  2940. var deferred = $.Deferred();
  2941. fetchInfoStub.returns(deferred.promise());
  2942. uploader.trigger('done', ev, data || {});
  2943. return deferred;
  2944. }
  2945. it('fetches file info', function() {
  2946. addFile(createUpload('upload.txt', '/subdir'));
  2947. expect(fetchInfoStub.calledOnce).toEqual(true);
  2948. expect(fetchInfoStub.getCall(0).args[0]).toEqual('upload.txt');
  2949. expect(fetchInfoStub.getCall(0).args[1]).toEqual('/subdir');
  2950. });
  2951. it('highlights all uploaded files after all fetches are done', function(done) {
  2952. var highlightStub = sinon.stub(fileList, 'highlightFiles');
  2953. var def1 = addFile(createUpload('upload.txt', '/subdir'));
  2954. var def2 = addFile(createUpload('upload2.txt', '/subdir'));
  2955. var def3 = addFile(createUpload('upload3.txt', '/another'));
  2956. uploader.trigger('stop', {});
  2957. expect(highlightStub.notCalled).toEqual(true);
  2958. def1.resolve();
  2959. expect(highlightStub.notCalled).toEqual(true);
  2960. def2.resolve();
  2961. def3.resolve();
  2962. setTimeout(function() {
  2963. expect(highlightStub.callCount).toEqual(1);
  2964. expect(highlightStub.getCall(0).args[0]).toEqual(['upload.txt', 'upload2.txt']);
  2965. highlightStub.restore();
  2966. done();
  2967. }, 5);
  2968. });
  2969. it('queries storage stats after all fetches are done', function(done) {
  2970. var statStub = sinon.stub(fileList, 'updateStorageStatistics');
  2971. var highlightStub = sinon.stub(fileList, 'highlightFiles');
  2972. var def1 = addFile(createUpload('upload.txt', '/subdir'));
  2973. var def2 = addFile(createUpload('upload2.txt', '/subdir'));
  2974. var def3 = addFile(createUpload('upload3.txt', '/another'));
  2975. uploader.trigger('stop', {});
  2976. expect(statStub.notCalled).toEqual(true);
  2977. def1.resolve();
  2978. expect(statStub.notCalled).toEqual(true);
  2979. def2.resolve();
  2980. def3.resolve();
  2981. setTimeout(function() {
  2982. expect(statStub.calledOnce).toEqual(true);
  2983. highlightStub.restore();
  2984. done();
  2985. }, 3);
  2986. });
  2987. });
  2988. });
  2989. describe('Handling errors', function () {
  2990. var deferredList;
  2991. var getFolderContentsStub;
  2992. var reloading;
  2993. beforeEach(function() {
  2994. deferredList = $.Deferred();
  2995. getFolderContentsStub =
  2996. sinon.stub(filesClient, 'getFolderContents');
  2997. getFolderContentsStub.onCall(0).returns(deferredList.promise());
  2998. getFolderContentsStub.onCall(1).returns($.Deferred().promise());
  2999. reloading = fileList.reload();
  3000. });
  3001. afterEach(function() {
  3002. getFolderContentsStub.restore();
  3003. fileList = undefined;
  3004. });
  3005. it('redirects to root folder in case of forbidden access', function (done) {
  3006. deferredList.reject(403);
  3007. return reloading.then(function() {
  3008. expect(fileList.getCurrentDirectory()).toEqual('/');
  3009. expect(getFolderContentsStub.calledTwice).toEqual(true);
  3010. }).then(done, done);
  3011. });
  3012. it('redirects to root folder and shows notification in case of internal server error', function (done) {
  3013. expect(notificationStub.notCalled).toEqual(true);
  3014. deferredList.reject(500);
  3015. return reloading.then(function() {
  3016. expect(fileList.getCurrentDirectory()).toEqual('/');
  3017. expect(getFolderContentsStub.calledTwice).toEqual(true);
  3018. expect(notificationStub.calledOnce).toEqual(true);
  3019. }).then(done, done);
  3020. });
  3021. it('redirects to root folder and shows notification in case of storage not available', function (done) {
  3022. expect(notificationStub.notCalled).toEqual(true);
  3023. deferredList.reject(503, 'Storage is temporarily not available');
  3024. return reloading.then(function() {
  3025. expect(fileList.getCurrentDirectory()).toEqual('/');
  3026. expect(getFolderContentsStub.calledTwice).toEqual(true);
  3027. expect(notificationStub.calledOnce).toEqual(true);
  3028. }).then(done, done);
  3029. });
  3030. });
  3031. describe('showFileBusyState', function() {
  3032. var $tr;
  3033. beforeEach(function() {
  3034. fileList.setFiles(testFiles);
  3035. $tr = fileList.findFileEl('Two.jpg');
  3036. });
  3037. it('shows spinner on busy rows', function() {
  3038. fileList.showFileBusyState('Two.jpg', true);
  3039. expect($tr.hasClass('busy')).toEqual(true);
  3040. expect($tr.find('.thumbnail').parent().attr('class'))
  3041. .toContain('icon-loading-small');
  3042. fileList.showFileBusyState('Two.jpg', false);
  3043. expect($tr.hasClass('busy')).toEqual(false);
  3044. expect(OC.TestUtil.getImageUrl($tr.find('.thumbnail')))
  3045. .toEqual(OC.imagePath('core', 'filetypes/image.svg'));
  3046. });
  3047. it('accepts multiple input formats', function() {
  3048. _.each([
  3049. 'Two.jpg',
  3050. ['Two.jpg'],
  3051. $tr,
  3052. [$tr]
  3053. ], function(testCase) {
  3054. fileList.showFileBusyState(testCase, true);
  3055. expect($tr.hasClass('busy')).toEqual(true);
  3056. fileList.showFileBusyState(testCase, false);
  3057. expect($tr.hasClass('busy')).toEqual(false);
  3058. });
  3059. });
  3060. });
  3061. describe('elementToFile', function() {
  3062. var $tr;
  3063. beforeEach(function() {
  3064. fileList.setFiles(testFiles);
  3065. $tr = fileList.findFileEl('One.txt');
  3066. });
  3067. it('converts data attributes to file info structure', function() {
  3068. var fileInfo = fileList.elementToFile($tr);
  3069. expect(fileInfo.id).toEqual(1);
  3070. expect(fileInfo.name).toEqual('One.txt');
  3071. expect(fileInfo.mtime).toEqual(123456789);
  3072. expect(fileInfo.etag).toEqual('abc');
  3073. expect(fileInfo.permissions).toEqual(OC.PERMISSION_ALL);
  3074. expect(fileInfo.size).toEqual(12);
  3075. expect(fileInfo.mimetype).toEqual('text/plain');
  3076. expect(fileInfo.type).toEqual('file');
  3077. expect(fileInfo.path).not.toBeDefined();
  3078. expect(fileInfo.isEncrypted).toEqual(false);
  3079. });
  3080. it('sets isEncrypted attribute if data includes true e2eencrypted', function() {
  3081. testFiles[3].isEncrypted = true;
  3082. fileList.setFiles(testFiles);
  3083. $tr = fileList.findFileEl('somedir');
  3084. var fileInfo = fileList.elementToFile($tr);
  3085. expect(fileInfo.isEncrypted).toEqual(true);
  3086. });
  3087. it('adds path attribute if available', function() {
  3088. $tr.attr('data-path', '/subdir');
  3089. var fileInfo = fileList.elementToFile($tr);
  3090. expect(fileInfo.path).toEqual('/subdir');
  3091. });
  3092. });
  3093. describe('new file menu', function() {
  3094. var newFileMenuStub;
  3095. beforeEach(function() {
  3096. newFileMenuStub = sinon.stub(OCA.Files.NewFileMenu.prototype, 'showAt');
  3097. });
  3098. afterEach(function() {
  3099. newFileMenuStub.restore();
  3100. })
  3101. it('renders new button when no legacy upload button exists', function() {
  3102. expect(fileList.$el.find('.button.upload').length).toEqual(0);
  3103. expect(fileList.$el.find('.button.new').length).toEqual(1);
  3104. });
  3105. it('does not render new button when no legacy upload button exists (public page)', function() {
  3106. fileList.destroy();
  3107. $('#controls').append('<input type="button" class="button upload" />');
  3108. fileList = new OCA.Files.FileList($('#app-content-files'));
  3109. expect(fileList.$el.find('.button.upload').length).toEqual(1);
  3110. expect(fileList.$el.find('.button.new').length).toEqual(0);
  3111. });
  3112. it('opens the new file menu when clicking on the "New" button', function() {
  3113. var $button = fileList.$el.find('.button.new');
  3114. $button.click();
  3115. expect(newFileMenuStub.calledOnce).toEqual(true);
  3116. });
  3117. it('does not open the new file menu when button is disabled', function() {
  3118. var $button = fileList.$el.find('.button.new');
  3119. $button.addClass('disabled');
  3120. $button.click();
  3121. expect(newFileMenuStub.notCalled).toEqual(true);
  3122. });
  3123. });
  3124. describe('mount type detection', function() {
  3125. function testMountType(dirInfoId, dirInfoMountType, inputMountType, expectedMountType) {
  3126. var $tr;
  3127. fileList.dirInfo.id = dirInfoId;
  3128. fileList.dirInfo.mountType = dirInfoMountType;
  3129. $tr = fileList.add({
  3130. type: 'dir',
  3131. mimetype: 'httpd/unix-directory',
  3132. name: 'test dir',
  3133. mountType: inputMountType
  3134. });
  3135. expect($tr.attr('data-mounttype')).toEqual(expectedMountType);
  3136. }
  3137. it('leaves mount type as is if no parent exists', function() {
  3138. testMountType(null, null, 'external', 'external');
  3139. testMountType(null, null, 'shared', 'shared');
  3140. });
  3141. it('detects share root if parent exists', function() {
  3142. testMountType(123, null, 'shared', 'shared-root');
  3143. testMountType(123, 'shared', 'shared', 'shared');
  3144. testMountType(123, 'shared-root', 'shared', 'shared');
  3145. });
  3146. it('detects external storage root if parent exists', function() {
  3147. testMountType(123, null, 'external', 'external-root');
  3148. testMountType(123, 'external', 'external', 'external');
  3149. testMountType(123, 'external-root', 'external', 'external');
  3150. });
  3151. });
  3152. describe('file list should not refresh if url does not change', function() {
  3153. var fileListStub;
  3154. beforeEach(function() {
  3155. fileListStub = sinon.stub(OCA.Files.FileList.prototype, 'changeDirectory');
  3156. fileList._currentDirectory = '/subdir';
  3157. });
  3158. afterEach(function() {
  3159. fileListStub.restore();
  3160. });
  3161. it('File list must not be refreshed', function() {
  3162. $('#app-content-files').trigger(new $.Event('urlChanged', {dir: '/subdir'}));
  3163. expect(fileListStub.notCalled).toEqual(true);
  3164. });
  3165. it('File list must be refreshed', function() {
  3166. $('#app-content-files').trigger(new $.Event('urlChanged', {dir: '/'}));
  3167. expect(fileListStub.notCalled).toEqual(false);
  3168. });
  3169. });
  3170. });