coreSpec.js 52 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601
  1. /**
  2. * ownCloud
  3. *
  4. * @author Vincent Petry
  5. * @copyright 2014 Vincent Petry <pvince81@owncloud.com>
  6. *
  7. * This library is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
  9. * License as published by the Free Software Foundation; either
  10. * version 3 of the License, or any later version.
  11. *
  12. * This library is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
  16. *
  17. * You should have received a copy of the GNU Affero General Public
  18. * License along with this library. If not, see <http://www.gnu.org/licenses/>.
  19. *
  20. */
  21. describe('Core base tests', function() {
  22. afterEach(function() {
  23. // many tests call window.initCore so need to unregister global events
  24. // ideally in the future we'll need a window.unloadCore() function
  25. $(document).off('ajaxError.main');
  26. $(document).off('unload.main');
  27. $(document).off('beforeunload.main');
  28. OC._userIsNavigatingAway = false;
  29. OC._reloadCalled = false;
  30. });
  31. describe('Base values', function() {
  32. it('Sets webroots', function() {
  33. expect(OC.getRootPath()).toBeDefined();
  34. expect(OC.appswebroots).toBeDefined();
  35. });
  36. });
  37. describe('basename', function() {
  38. it('Returns the nothing if no file name given', function() {
  39. expect(OC.basename('')).toEqual('');
  40. });
  41. it('Returns the nothing if dir is root', function() {
  42. expect(OC.basename('/')).toEqual('');
  43. });
  44. it('Returns the same name if no path given', function() {
  45. expect(OC.basename('some name.txt')).toEqual('some name.txt');
  46. });
  47. it('Returns the base name if root path given', function() {
  48. expect(OC.basename('/some name.txt')).toEqual('some name.txt');
  49. });
  50. it('Returns the base name if double root path given', function() {
  51. expect(OC.basename('//some name.txt')).toEqual('some name.txt');
  52. });
  53. it('Returns the base name if subdir given without root', function() {
  54. expect(OC.basename('subdir/some name.txt')).toEqual('some name.txt');
  55. });
  56. it('Returns the base name if subdir given with root', function() {
  57. expect(OC.basename('/subdir/some name.txt')).toEqual('some name.txt');
  58. });
  59. it('Returns the base name if subdir given with double root', function() {
  60. expect(OC.basename('//subdir/some name.txt')).toEqual('some name.txt');
  61. });
  62. it('Returns the base name if subdir has dot', function() {
  63. expect(OC.basename('/subdir.dat/some name.txt')).toEqual('some name.txt');
  64. });
  65. it('Returns dot if file name is dot', function() {
  66. expect(OC.basename('/subdir/.')).toEqual('.');
  67. });
  68. // TODO: fix the source to make it work like PHP's basename
  69. it('Returns the dir itself if no file name given', function() {
  70. // TODO: fix the source to make it work like PHP's dirname
  71. // expect(OC.basename('subdir/')).toEqual('subdir');
  72. expect(OC.basename('subdir/')).toEqual('');
  73. });
  74. it('Returns the dir itself if no file name given with root', function() {
  75. // TODO: fix the source to make it work like PHP's dirname
  76. // expect(OC.basename('/subdir/')).toEqual('subdir');
  77. expect(OC.basename('/subdir/')).toEqual('');
  78. });
  79. });
  80. describe('dirname', function() {
  81. it('Returns the nothing if no file name given', function() {
  82. expect(OC.dirname('')).toEqual('');
  83. });
  84. it('Returns the root if dir is root', function() {
  85. // TODO: fix the source to make it work like PHP's dirname
  86. // expect(OC.dirname('/')).toEqual('/');
  87. expect(OC.dirname('/')).toEqual('');
  88. });
  89. it('Returns the root if dir is double root', function() {
  90. // TODO: fix the source to make it work like PHP's dirname
  91. // expect(OC.dirname('//')).toEqual('/');
  92. expect(OC.dirname('//')).toEqual('/'); // oh no...
  93. });
  94. it('Returns dot if dir is dot', function() {
  95. expect(OC.dirname('.')).toEqual('.');
  96. });
  97. it('Returns dot if no root given', function() {
  98. // TODO: fix the source to make it work like PHP's dirname
  99. // expect(OC.dirname('some dir')).toEqual('.');
  100. expect(OC.dirname('some dir')).toEqual('some dir'); // oh no...
  101. });
  102. it('Returns the dir name if file name and root path given', function() {
  103. // TODO: fix the source to make it work like PHP's dirname
  104. // expect(OC.dirname('/some name.txt')).toEqual('/');
  105. expect(OC.dirname('/some name.txt')).toEqual('');
  106. });
  107. it('Returns the dir name if double root path given', function() {
  108. expect(OC.dirname('//some name.txt')).toEqual('/'); // how lucky...
  109. });
  110. it('Returns the dir name if subdir given without root', function() {
  111. expect(OC.dirname('subdir/some name.txt')).toEqual('subdir');
  112. });
  113. it('Returns the dir name if subdir given with root', function() {
  114. expect(OC.dirname('/subdir/some name.txt')).toEqual('/subdir');
  115. });
  116. it('Returns the dir name if subdir given with double root', function() {
  117. // TODO: fix the source to make it work like PHP's dirname
  118. // expect(OC.dirname('//subdir/some name.txt')).toEqual('/subdir');
  119. expect(OC.dirname('//subdir/some name.txt')).toEqual('//subdir'); // oh...
  120. });
  121. it('Returns the dir name if subdir has dot', function() {
  122. expect(OC.dirname('/subdir.dat/some name.txt')).toEqual('/subdir.dat');
  123. });
  124. it('Returns the dir name if file name is dot', function() {
  125. expect(OC.dirname('/subdir/.')).toEqual('/subdir');
  126. });
  127. it('Returns the dir name if no file name given', function() {
  128. expect(OC.dirname('subdir/')).toEqual('subdir');
  129. });
  130. it('Returns the dir name if no file name given with root', function() {
  131. expect(OC.dirname('/subdir/')).toEqual('/subdir');
  132. });
  133. });
  134. describe('escapeHTML', function() {
  135. it('Returns nothing if no string was given', function() {
  136. expect(escapeHTML('')).toEqual('');
  137. });
  138. it('Returns a sanitized string if a string containing HTML is given', function() {
  139. expect(escapeHTML('There needs to be a <script>alert(\"Unit\" + \'test\')</script> for it!')).toEqual('There needs to be a &lt;script&gt;alert(&quot;Unit&quot; + &#039;test&#039;)&lt;/script&gt; for it!');
  140. });
  141. it('Returns the string without modification if no potentially dangerous character is passed.', function() {
  142. expect(escapeHTML('This is a good string without HTML.')).toEqual('This is a good string without HTML.');
  143. });
  144. });
  145. describe('joinPaths', function() {
  146. it('returns empty string with no or empty arguments', function() {
  147. expect(OC.joinPaths()).toEqual('');
  148. expect(OC.joinPaths('')).toEqual('');
  149. expect(OC.joinPaths('', '')).toEqual('');
  150. });
  151. it('returns joined path sections', function() {
  152. expect(OC.joinPaths('abc')).toEqual('abc');
  153. expect(OC.joinPaths('abc', 'def')).toEqual('abc/def');
  154. expect(OC.joinPaths('abc', 'def', 'ghi')).toEqual('abc/def/ghi');
  155. });
  156. it('keeps leading slashes', function() {
  157. expect(OC.joinPaths('/abc')).toEqual('/abc');
  158. expect(OC.joinPaths('/abc', '')).toEqual('/abc');
  159. expect(OC.joinPaths('', '/abc')).toEqual('/abc');
  160. expect(OC.joinPaths('/abc', 'def')).toEqual('/abc/def');
  161. expect(OC.joinPaths('/abc', 'def', 'ghi')).toEqual('/abc/def/ghi');
  162. });
  163. it('keeps trailing slashes', function() {
  164. expect(OC.joinPaths('', 'abc/')).toEqual('abc/');
  165. expect(OC.joinPaths('abc/')).toEqual('abc/');
  166. expect(OC.joinPaths('abc/', '')).toEqual('abc/');
  167. expect(OC.joinPaths('abc', 'def/')).toEqual('abc/def/');
  168. expect(OC.joinPaths('abc', 'def', 'ghi/')).toEqual('abc/def/ghi/');
  169. });
  170. it('splits paths in specified strings and discards extra slashes', function() {
  171. expect(OC.joinPaths('//abc//')).toEqual('/abc/');
  172. expect(OC.joinPaths('//abc//def//')).toEqual('/abc/def/');
  173. expect(OC.joinPaths('//abc//', '//def//')).toEqual('/abc/def/');
  174. expect(OC.joinPaths('//abc//', '//def//', '//ghi//')).toEqual('/abc/def/ghi/');
  175. expect(OC.joinPaths('//abc//def//', '//ghi//jkl/mno/', '//pqr//'))
  176. .toEqual('/abc/def/ghi/jkl/mno/pqr/');
  177. expect(OC.joinPaths('/abc', '/def')).toEqual('/abc/def');
  178. expect(OC.joinPaths('/abc/', '/def')).toEqual('/abc/def');
  179. expect(OC.joinPaths('/abc/', 'def')).toEqual('/abc/def');
  180. });
  181. it('discards empty sections', function() {
  182. expect(OC.joinPaths('abc', '', 'def')).toEqual('abc/def');
  183. });
  184. it('returns root if only slashes', function() {
  185. expect(OC.joinPaths('//')).toEqual('/');
  186. expect(OC.joinPaths('/', '/')).toEqual('/');
  187. expect(OC.joinPaths('/', '//', '/')).toEqual('/');
  188. });
  189. });
  190. describe('isSamePath', function() {
  191. it('recognizes empty paths are equal', function() {
  192. expect(OC.isSamePath('', '')).toEqual(true);
  193. expect(OC.isSamePath('/', '')).toEqual(true);
  194. expect(OC.isSamePath('//', '')).toEqual(true);
  195. expect(OC.isSamePath('/', '/')).toEqual(true);
  196. expect(OC.isSamePath('/', '//')).toEqual(true);
  197. });
  198. it('recognizes path with single sections as equal regardless of extra slashes', function() {
  199. expect(OC.isSamePath('abc', 'abc')).toEqual(true);
  200. expect(OC.isSamePath('/abc', 'abc')).toEqual(true);
  201. expect(OC.isSamePath('//abc', 'abc')).toEqual(true);
  202. expect(OC.isSamePath('abc', '/abc')).toEqual(true);
  203. expect(OC.isSamePath('abc/', 'abc')).toEqual(true);
  204. expect(OC.isSamePath('abc/', 'abc/')).toEqual(true);
  205. expect(OC.isSamePath('/abc/', 'abc/')).toEqual(true);
  206. expect(OC.isSamePath('/abc/', '/abc/')).toEqual(true);
  207. expect(OC.isSamePath('//abc/', '/abc/')).toEqual(true);
  208. expect(OC.isSamePath('//abc//', '/abc/')).toEqual(true);
  209. expect(OC.isSamePath('abc', 'def')).toEqual(false);
  210. expect(OC.isSamePath('/abc', 'def')).toEqual(false);
  211. expect(OC.isSamePath('//abc', 'def')).toEqual(false);
  212. expect(OC.isSamePath('abc', '/def')).toEqual(false);
  213. expect(OC.isSamePath('abc/', 'def')).toEqual(false);
  214. expect(OC.isSamePath('abc/', 'def/')).toEqual(false);
  215. expect(OC.isSamePath('/abc/', 'def/')).toEqual(false);
  216. expect(OC.isSamePath('/abc/', '/def/')).toEqual(false);
  217. expect(OC.isSamePath('//abc/', '/def/')).toEqual(false);
  218. expect(OC.isSamePath('//abc//', '/def/')).toEqual(false);
  219. });
  220. it('recognizes path with multiple sections as equal regardless of extra slashes', function() {
  221. expect(OC.isSamePath('abc/def', 'abc/def')).toEqual(true);
  222. expect(OC.isSamePath('/abc/def', 'abc/def')).toEqual(true);
  223. expect(OC.isSamePath('abc/def', '/abc/def')).toEqual(true);
  224. expect(OC.isSamePath('abc/def/', '/abc/def/')).toEqual(true);
  225. expect(OC.isSamePath('/abc/def/', '/abc/def/')).toEqual(true);
  226. expect(OC.isSamePath('/abc/def/', 'abc/def/')).toEqual(true);
  227. expect(OC.isSamePath('//abc/def/', 'abc/def/')).toEqual(true);
  228. expect(OC.isSamePath('//abc/def//', 'abc/def/')).toEqual(true);
  229. expect(OC.isSamePath('abc/def', 'abc/ghi')).toEqual(false);
  230. expect(OC.isSamePath('/abc/def', 'abc/ghi')).toEqual(false);
  231. expect(OC.isSamePath('abc/def', '/abc/ghi')).toEqual(false);
  232. expect(OC.isSamePath('abc/def/', '/abc/ghi/')).toEqual(false);
  233. expect(OC.isSamePath('/abc/def/', '/abc/ghi/')).toEqual(false);
  234. expect(OC.isSamePath('/abc/def/', 'abc/ghi/')).toEqual(false);
  235. expect(OC.isSamePath('//abc/def/', 'abc/ghi/')).toEqual(false);
  236. expect(OC.isSamePath('//abc/def//', 'abc/ghi/')).toEqual(false);
  237. });
  238. it('recognizes path entries with dot', function() {
  239. expect(OC.isSamePath('.', '')).toEqual(true);
  240. expect(OC.isSamePath('.', '.')).toEqual(true);
  241. expect(OC.isSamePath('.', '/')).toEqual(true);
  242. expect(OC.isSamePath('/.', '/')).toEqual(true);
  243. expect(OC.isSamePath('/./', '/')).toEqual(true);
  244. expect(OC.isSamePath('/./', '/.')).toEqual(true);
  245. expect(OC.isSamePath('/./', '/./')).toEqual(true);
  246. expect(OC.isSamePath('/./', '/./')).toEqual(true);
  247. expect(OC.isSamePath('a/./b', 'a/b')).toEqual(true);
  248. expect(OC.isSamePath('a/b/.', 'a/b')).toEqual(true);
  249. expect(OC.isSamePath('./a/b', 'a/b')).toEqual(true);
  250. });
  251. });
  252. describe('filePath', function() {
  253. beforeEach(function() {
  254. OC.webroot = 'http://localhost';
  255. OC.appswebroots.files = OC.getRootPath() + '/apps3/files';
  256. });
  257. afterEach(function() {
  258. delete OC.appswebroots.files;
  259. });
  260. it('Uses a direct link for css and images,' , function() {
  261. expect(OC.filePath('core', 'css', 'style.css')).toEqual('http://localhost/core/css/style.css');
  262. expect(OC.filePath('files', 'css', 'style.css')).toEqual('http://localhost/apps3/files/css/style.css');
  263. expect(OC.filePath('core', 'img', 'image.png')).toEqual('http://localhost/core/img/image.png');
  264. expect(OC.filePath('files', 'img', 'image.png')).toEqual('http://localhost/apps3/files/img/image.png');
  265. });
  266. it('Routes PHP files via index.php,' , function() {
  267. expect(OC.filePath('core', 'ajax', 'test.php')).toEqual('http://localhost/index.php/core/ajax/test.php');
  268. expect(OC.filePath('files', 'ajax', 'test.php')).toEqual('http://localhost/index.php/apps/files/ajax/test.php');
  269. });
  270. });
  271. describe('getCanonicalLocale', function() {
  272. var oldLocale;
  273. beforeEach(function() {
  274. oldLocale = $('html').data('locale')
  275. });
  276. afterEach(function() {
  277. $('html').data('locale', oldLocale)
  278. });
  279. it("Returns primary locales as is", function() {
  280. $('html').data('locale', 'de')
  281. expect(OC.getCanonicalLocale()).toEqual('de');
  282. $('html').data('locale', 'zu')
  283. expect(OC.getCanonicalLocale()).toEqual('zu');
  284. });
  285. it("Returns extended locales with hyphens", function() {
  286. $('html').data('locale', 'az_Cyrl_AZ')
  287. expect(OC.getCanonicalLocale()).toEqual('az-Cyrl-AZ');
  288. $('html').data('locale', 'de_DE')
  289. expect(OC.getCanonicalLocale()).toEqual('de-DE');
  290. });
  291. });
  292. describe('Link functions', function() {
  293. var TESTAPP = 'testapp';
  294. var TESTAPP_ROOT = OC.getRootPath() + '/appsx/testapp';
  295. beforeEach(function() {
  296. OC.appswebroots[TESTAPP] = TESTAPP_ROOT;
  297. });
  298. afterEach(function() {
  299. // restore original array
  300. delete OC.appswebroots[TESTAPP];
  301. });
  302. it('Generates correct links for core apps', function() {
  303. expect(OC.linkTo('core', 'somefile.php')).toEqual(OC.getRootPath() + '/core/somefile.php');
  304. expect(OC.linkTo('admin', 'somefile.php')).toEqual(OC.getRootPath() + '/admin/somefile.php');
  305. });
  306. it('Generates correct links for regular apps', function() {
  307. expect(OC.linkTo(TESTAPP, 'somefile.php')).toEqual(OC.getRootPath() + '/index.php/apps/' + TESTAPP + '/somefile.php');
  308. });
  309. it('Generates correct remote links', function() {
  310. expect(OC.linkToRemote('webdav')).toEqual(window.location.protocol + '//' + window.location.host + OC.getRootPath() + '/remote.php/webdav');
  311. });
  312. describe('Images', function() {
  313. it('Generates image path with given extension', function() {
  314. expect(OC.imagePath('core', 'somefile.jpg')).toEqual(OC.getRootPath() + '/core/img/somefile.jpg');
  315. expect(OC.imagePath(TESTAPP, 'somefile.jpg')).toEqual(TESTAPP_ROOT + '/img/somefile.jpg');
  316. });
  317. it('Generates image path with svg extension', function() {
  318. expect(OC.imagePath('core', 'somefile')).toEqual(OC.getRootPath() + '/core/img/somefile.svg');
  319. expect(OC.imagePath(TESTAPP, 'somefile')).toEqual(TESTAPP_ROOT + '/img/somefile.svg');
  320. });
  321. });
  322. });
  323. describe('Query string building', function() {
  324. it('Returns empty string when empty params', function() {
  325. expect(OC.buildQueryString()).toEqual('');
  326. expect(OC.buildQueryString({})).toEqual('');
  327. });
  328. it('Encodes regular query strings', function() {
  329. expect(OC.buildQueryString({
  330. a: 'abc',
  331. b: 'def'
  332. })).toEqual('a=abc&b=def');
  333. });
  334. it('Encodes special characters', function() {
  335. expect(OC.buildQueryString({
  336. unicode: '汉字'
  337. })).toEqual('unicode=%E6%B1%89%E5%AD%97');
  338. expect(OC.buildQueryString({
  339. b: 'spaace value',
  340. 'space key': 'normalvalue',
  341. 'slash/this': 'amp&ersand'
  342. })).toEqual('b=spaace%20value&space%20key=normalvalue&slash%2Fthis=amp%26ersand');
  343. });
  344. it('Encodes data types and empty values', function() {
  345. expect(OC.buildQueryString({
  346. 'keywithemptystring': '',
  347. 'keywithnull': null,
  348. 'keywithundefined': null,
  349. something: 'else'
  350. })).toEqual('keywithemptystring=&keywithnull&keywithundefined&something=else');
  351. expect(OC.buildQueryString({
  352. 'booleanfalse': false,
  353. 'booleantrue': true
  354. })).toEqual('booleanfalse=false&booleantrue=true');
  355. expect(OC.buildQueryString({
  356. 'number': 123
  357. })).toEqual('number=123');
  358. });
  359. });
  360. describe('Session heartbeat', function() {
  361. var clock,
  362. oldConfig,
  363. counter;
  364. beforeEach(function() {
  365. clock = sinon.useFakeTimers();
  366. oldConfig = OC.config;
  367. counter = 0;
  368. fakeServer.autoRespond = true;
  369. fakeServer.autoRespondAfter = 0;
  370. fakeServer.respondWith(/\/csrftoken/, function(xhr) {
  371. counter++;
  372. xhr.respond(200, {'Content-Type': 'application/json'}, '{"token": "pgBEsb3MzTb1ZPd2mfDZbQ6/0j3OrXHMEZrghHcOkg8=:3khw5PSa+wKQVo4f26exFD3nplud9ECjJ8/Y5zk5/k4="}');
  373. });
  374. $(document).off('ajaxComplete'); // ignore previously registered heartbeats
  375. });
  376. afterEach(function() {
  377. clock.restore();
  378. /* jshint camelcase: false */
  379. OC.config = oldConfig;
  380. $(document).off('ajaxError');
  381. $(document).off('ajaxComplete');
  382. });
  383. it('sends heartbeat half the session lifetime when heartbeat enabled', function() {
  384. /* jshint camelcase: false */
  385. OC.config = {
  386. session_keepalive: true,
  387. session_lifetime: 300
  388. };
  389. window.initCore();
  390. expect(counter).toEqual(0);
  391. // less than half, still nothing
  392. clock.tick(100 * 1000);
  393. expect(counter).toEqual(0);
  394. // reach past half (160), one call
  395. clock.tick(55 * 1000);
  396. expect(counter).toEqual(1);
  397. // almost there to the next, still one
  398. clock.tick(140 * 1000);
  399. expect(counter).toEqual(1);
  400. // past it, second call
  401. clock.tick(20 * 1000);
  402. expect(counter).toEqual(2);
  403. });
  404. it('does not send heartbeat when heartbeat disabled', function() {
  405. /* jshint camelcase: false */
  406. OC.config = {
  407. session_keepalive: false,
  408. session_lifetime: 300
  409. };
  410. window.initCore();
  411. expect(counter).toEqual(0);
  412. clock.tick(1000000);
  413. // still nothing
  414. expect(counter).toEqual(0);
  415. });
  416. it('limits the heartbeat between one minute and one day', function() {
  417. /* jshint camelcase: false */
  418. var setIntervalStub = sinon.stub(window, 'setInterval');
  419. OC.config = {
  420. session_keepalive: true,
  421. session_lifetime: 5
  422. };
  423. window.initCore();
  424. expect(setIntervalStub.getCall(0).args[1]).toEqual(60 * 1000);
  425. setIntervalStub.reset();
  426. OC.config = {
  427. session_keepalive: true,
  428. session_lifetime: 48 * 3600
  429. };
  430. window.initCore();
  431. expect(setIntervalStub.getCall(0).args[1]).toEqual(24 * 3600 * 1000);
  432. setIntervalStub.restore();
  433. });
  434. });
  435. describe('Parse query string', function() {
  436. it('Parses query string from full URL', function() {
  437. var query = OC.parseQueryString('http://localhost/stuff.php?q=a&b=x');
  438. expect(query).toEqual({q: 'a', b: 'x'});
  439. });
  440. it('Parses query string from query part alone', function() {
  441. var query = OC.parseQueryString('q=a&b=x');
  442. expect(query).toEqual({q: 'a', b: 'x'});
  443. });
  444. it('Returns null hash when empty query', function() {
  445. var query = OC.parseQueryString('');
  446. expect(query).toEqual(null);
  447. });
  448. it('Returns empty hash when empty query with question mark', function() {
  449. var query = OC.parseQueryString('?');
  450. expect(query).toEqual({});
  451. });
  452. it('Decodes regular query strings', function() {
  453. var query = OC.parseQueryString('a=abc&b=def');
  454. expect(query).toEqual({
  455. a: 'abc',
  456. b: 'def'
  457. });
  458. });
  459. it('Ignores empty parts', function() {
  460. var query = OC.parseQueryString('&q=a&&b=x&');
  461. expect(query).toEqual({q: 'a', b: 'x'});
  462. });
  463. it('Ignores lone equal signs', function() {
  464. var query = OC.parseQueryString('&q=a&=&b=x&');
  465. expect(query).toEqual({q: 'a', b: 'x'});
  466. });
  467. it('Includes extra equal signs in value', function() {
  468. var query = OC.parseQueryString('u=a=x&q=a=b');
  469. expect(query).toEqual({u: 'a=x', q: 'a=b'});
  470. });
  471. it('Decodes plus as space', function() {
  472. var query = OC.parseQueryString('space+key=space+value');
  473. expect(query).toEqual({'space key': 'space value'});
  474. });
  475. it('Decodes special characters', function() {
  476. var query = OC.parseQueryString('unicode=%E6%B1%89%E5%AD%97');
  477. expect(query).toEqual({unicode: '汉字'});
  478. query = OC.parseQueryString('b=spaace%20value&space%20key=normalvalue&slash%2Fthis=amp%26ersand');
  479. expect(query).toEqual({
  480. b: 'spaace value',
  481. 'space key': 'normalvalue',
  482. 'slash/this': 'amp&ersand'
  483. });
  484. });
  485. it('Decodes empty values', function() {
  486. var query = OC.parseQueryString('keywithemptystring=&keywithnostring');
  487. expect(query).toEqual({
  488. 'keywithemptystring': '',
  489. 'keywithnostring': null
  490. });
  491. });
  492. it('Does not interpret data types', function() {
  493. var query = OC.parseQueryString('booleanfalse=false&booleantrue=true&number=123');
  494. expect(query).toEqual({
  495. 'booleanfalse': 'false',
  496. 'booleantrue': 'true',
  497. 'number': '123'
  498. });
  499. });
  500. });
  501. describe('Generate Url', function() {
  502. it('returns absolute urls', function() {
  503. expect(OC.generateUrl('csrftoken')).toEqual(OC.getRootPath() + '/index.php/csrftoken');
  504. expect(OC.generateUrl('/csrftoken')).toEqual(OC.getRootPath() + '/index.php/csrftoken');
  505. });
  506. it('substitutes parameters which are escaped by default', function() {
  507. expect(OC.generateUrl('apps/files/download/{file}', {file: '<">ImAnUnescapedString/!'})).toEqual(OC.getRootPath() + '/index.php/apps/files/download/%3C%22%3EImAnUnescapedString%2F!');
  508. });
  509. it('substitutes parameters which can also be unescaped via option flag', function() {
  510. expect(OC.generateUrl('apps/files/download/{file}', {file: 'subfolder/Welcome.txt'}, {escape: false})).toEqual(OC.getRootPath() + '/index.php/apps/files/download/subfolder/Welcome.txt');
  511. });
  512. it('substitutes multiple parameters which are escaped by default', function() {
  513. expect(OC.generateUrl('apps/files/download/{file}/{id}', {file: '<">ImAnUnescapedString/!', id: 5})).toEqual(OC.getRootPath() + '/index.php/apps/files/download/%3C%22%3EImAnUnescapedString%2F!/5');
  514. });
  515. it('substitutes multiple parameters which can also be unescaped via option flag', function() {
  516. expect(OC.generateUrl('apps/files/download/{file}/{id}', {file: 'subfolder/Welcome.txt', id: 5}, {escape: false})).toEqual(OC.getRootPath() + '/index.php/apps/files/download/subfolder/Welcome.txt/5');
  517. });
  518. it('doesnt error out with no params provided', function () {
  519. expect(OC.generateUrl('apps/files/download{file}')).toEqual(OC.getRootPath() + '/index.php/apps/files/download%7Bfile%7D');
  520. });
  521. });
  522. describe('Main menu mobile toggle', function() {
  523. var clock;
  524. var $toggle;
  525. var $navigation;
  526. beforeEach(function() {
  527. jQuery.fx.off = true;
  528. clock = sinon.useFakeTimers();
  529. $('#testArea').append('<div id="header">' +
  530. '<a class="menutoggle header-appname-container" href="#">' +
  531. '<h1 class="header-appname"></h1>' +
  532. '<div class="icon-caret"></div>' +
  533. '</a>' +
  534. '</div>' +
  535. '<div id="navigation"></div>');
  536. $toggle = $('#header').find('.menutoggle');
  537. $navigation = $('#navigation');
  538. });
  539. afterEach(function() {
  540. jQuery.fx.off = false;
  541. clock.restore();
  542. $(document).off('ajaxError');
  543. });
  544. it('Sets up menu toggle', function() {
  545. window.initCore();
  546. expect($navigation.hasClass('menu')).toEqual(true);
  547. });
  548. it('Clicking menu toggle toggles navigation in', function() {
  549. window.initCore();
  550. // fore show more apps icon since otherwise it would be hidden since no icons are available
  551. clock.tick(1 * 1000);
  552. $('#more-apps').show();
  553. expect($navigation.is(':visible')).toEqual(false);
  554. $toggle.click();
  555. clock.tick(1 * 1000);
  556. expect($navigation.is(':visible')).toEqual(true);
  557. $toggle.click();
  558. clock.tick(1 * 1000);
  559. expect($navigation.is(':visible')).toEqual(false);
  560. });
  561. });
  562. describe('Util', function() {
  563. var locale;
  564. var localeStub;
  565. beforeEach(function() {
  566. locale = OC.getCanonicalLocale();
  567. localeStub = sinon.stub(OC, 'getCanonicalLocale');
  568. localeStub.returns(locale);
  569. });
  570. afterEach(function() {
  571. localeStub.restore();
  572. });
  573. describe('humanFileSize', function() {
  574. // cit() will skip tests if toLocaleString() is not supported.
  575. // See https://github.com/ariya/phantomjs/issues/12581
  576. //
  577. // Please run these tests in Chrome/Firefox manually.
  578. var cit = 4.2.toLocaleString("de") !== "4,2" ? xit : it;
  579. it('renders file sizes with the correct unit', function() {
  580. var data = [
  581. [0, '0 B'],
  582. ["0", '0 B'],
  583. ["A", 'NaN B'],
  584. [125, '125 B'],
  585. [128000, '125 KB'],
  586. [128000000, '122.1 MB'],
  587. [128000000000, '119.2 GB'],
  588. [128000000000000, '116.4 TB']
  589. ];
  590. for (var i = 0; i < data.length; i++) {
  591. expect(OC.Util.humanFileSize(data[i][0])).toEqual(data[i][1]);
  592. }
  593. });
  594. it('renders file sizes with the correct unit for small sizes', function() {
  595. var data = [
  596. [0, '0 KB'],
  597. [125, '< 1 KB'],
  598. [128000, '125 KB'],
  599. [128000000, '122.1 MB'],
  600. [128000000000, '119.2 GB'],
  601. [128000000000000, '116.4 TB']
  602. ];
  603. for (var i = 0; i < data.length; i++) {
  604. expect(OC.Util.humanFileSize(data[i][0], true)).toEqual(data[i][1]);
  605. }
  606. });
  607. cit('renders file sizes with the correct locale', function() {
  608. localeStub.returns("de");
  609. var data = [
  610. [0, '0 B'],
  611. ["0", '0 B'],
  612. ["A", 'NaN B'],
  613. [125, '125 B'],
  614. [128000, '125 KB'],
  615. [128000000, '122,1 MB'],
  616. [128000000000, '119,2 GB'],
  617. [128000000000000, '116,4 TB']
  618. ];
  619. for (var i = 0; i < data.length; i++) {
  620. expect(OC.Util.humanFileSize(data[i][0])).toEqual(data[i][1]);
  621. }
  622. });
  623. });
  624. describe('computerFileSize', function() {
  625. it('correctly parses file sizes from a human readable formated string', function() {
  626. var data = [
  627. ['125', 125],
  628. ['125.25', 125],
  629. ['125.25B', 125],
  630. ['125.25 B', 125],
  631. ['0 B', 0],
  632. ['99999999999999999999999999999999999999999999 B', 99999999999999999999999999999999999999999999],
  633. ['0 MB', 0],
  634. ['0 kB', 0],
  635. ['0kB', 0],
  636. ['125 B', 125],
  637. ['125b', 125],
  638. ['125 KB', 128000],
  639. ['125kb', 128000],
  640. ['122.1 MB', 128031130],
  641. ['122.1mb', 128031130],
  642. ['119.2 GB', 127990025421],
  643. ['119.2gb', 127990025421],
  644. ['116.4 TB', 127983153473126],
  645. ['116.4tb', 127983153473126],
  646. ['8776656778888777655.4tb', 9.650036181387265e+30],
  647. [1234, null],
  648. [-1234, null],
  649. ['-1234 B', null],
  650. ['B', null],
  651. ['40/0', null],
  652. ['40,30 kb', null],
  653. [' 122.1 MB ', 128031130],
  654. ['122.1 MB ', 128031130],
  655. [' 122.1 MB ', 128031130],
  656. [' 122.1 MB ', 128031130],
  657. ['122.1 MB ', 128031130],
  658. [' 125', 125],
  659. [' 125 ', 125],
  660. ];
  661. for (var i = 0; i < data.length; i++) {
  662. expect(OC.Util.computerFileSize(data[i][0])).toEqual(data[i][1]);
  663. }
  664. });
  665. it('returns null if the parameter is not a string', function() {
  666. expect(OC.Util.computerFileSize(NaN)).toEqual(null);
  667. expect(OC.Util.computerFileSize(125)).toEqual(null);
  668. });
  669. it('returns null if the string is unparsable', function() {
  670. expect(OC.Util.computerFileSize('')).toEqual(null);
  671. expect(OC.Util.computerFileSize('foobar')).toEqual(null);
  672. });
  673. });
  674. describe('stripTime', function() {
  675. it('strips time from dates', function() {
  676. expect(OC.Util.stripTime(new Date(2014, 2, 24, 15, 4, 45, 24)))
  677. .toEqual(new Date(2014, 2, 24, 0, 0, 0, 0));
  678. });
  679. });
  680. });
  681. describe('naturalSortCompare', function() {
  682. // cit() will skip tests if running on PhantomJS because it has issues with
  683. // localeCompare(). See https://github.com/ariya/phantomjs/issues/11063
  684. //
  685. // Please make sure to run these tests in Chrome/Firefox manually
  686. // to make sure they run.
  687. var cit = window.isPhantom?xit:it;
  688. // must provide the same results as \OC_Util::naturalSortCompare
  689. it('sorts alphabetically', function() {
  690. var a = [
  691. 'def',
  692. 'xyz',
  693. 'abc'
  694. ];
  695. a.sort(OC.Util.naturalSortCompare);
  696. expect(a).toEqual([
  697. 'abc',
  698. 'def',
  699. 'xyz'
  700. ]);
  701. });
  702. cit('sorts with different casing', function() {
  703. var a = [
  704. 'aaa',
  705. 'bbb',
  706. 'BBB',
  707. 'AAA'
  708. ];
  709. a.sort(OC.Util.naturalSortCompare);
  710. expect(a).toEqual([
  711. 'aaa',
  712. 'AAA',
  713. 'bbb',
  714. 'BBB'
  715. ]);
  716. });
  717. it('sorts with numbers', function() {
  718. var a = [
  719. '124.txt',
  720. 'abc1',
  721. '123.txt',
  722. 'abc',
  723. 'abc2',
  724. 'def (2).txt',
  725. 'ghi 10.txt',
  726. 'abc12',
  727. 'def.txt',
  728. 'def (1).txt',
  729. 'ghi 2.txt',
  730. 'def (10).txt',
  731. 'abc10',
  732. 'def (12).txt',
  733. 'z',
  734. 'ghi.txt',
  735. 'za',
  736. 'ghi 1.txt',
  737. 'ghi 12.txt',
  738. 'zz',
  739. '15.txt',
  740. '15b.txt'
  741. ];
  742. a.sort(OC.Util.naturalSortCompare);
  743. expect(a).toEqual([
  744. '15.txt',
  745. '15b.txt',
  746. '123.txt',
  747. '124.txt',
  748. 'abc',
  749. 'abc1',
  750. 'abc2',
  751. 'abc10',
  752. 'abc12',
  753. 'def.txt',
  754. 'def (1).txt',
  755. 'def (2).txt',
  756. 'def (10).txt',
  757. 'def (12).txt',
  758. 'ghi.txt',
  759. 'ghi 1.txt',
  760. 'ghi 2.txt',
  761. 'ghi 10.txt',
  762. 'ghi 12.txt',
  763. 'z',
  764. 'za',
  765. 'zz'
  766. ]);
  767. });
  768. it('sorts with chinese characters', function() {
  769. var a = [
  770. '十.txt',
  771. '一.txt',
  772. '二.txt',
  773. '十 2.txt',
  774. '三.txt',
  775. '四.txt',
  776. 'abc.txt',
  777. '五.txt',
  778. '七.txt',
  779. '八.txt',
  780. '九.txt',
  781. '六.txt',
  782. '十一.txt',
  783. '波.txt',
  784. '破.txt',
  785. '莫.txt',
  786. '啊.txt',
  787. '123.txt'
  788. ];
  789. a.sort(OC.Util.naturalSortCompare);
  790. expect(a).toEqual([
  791. '123.txt',
  792. 'abc.txt',
  793. '一.txt',
  794. '七.txt',
  795. '三.txt',
  796. '九.txt',
  797. '二.txt',
  798. '五.txt',
  799. '八.txt',
  800. '六.txt',
  801. '十.txt',
  802. '十 2.txt',
  803. '十一.txt',
  804. '啊.txt',
  805. '四.txt',
  806. '波.txt',
  807. '破.txt',
  808. '莫.txt'
  809. ]);
  810. });
  811. cit('sorts with umlauts', function() {
  812. var a = [
  813. 'öh.txt',
  814. 'Äh.txt',
  815. 'oh.txt',
  816. 'Üh 2.txt',
  817. 'Üh.txt',
  818. 'ah.txt',
  819. 'Öh.txt',
  820. 'uh.txt',
  821. 'üh.txt',
  822. 'äh.txt'
  823. ];
  824. a.sort(OC.Util.naturalSortCompare);
  825. expect(a).toEqual([
  826. 'ah.txt',
  827. 'äh.txt',
  828. 'Äh.txt',
  829. 'oh.txt',
  830. 'öh.txt',
  831. 'Öh.txt',
  832. 'uh.txt',
  833. 'üh.txt',
  834. 'Üh.txt',
  835. 'Üh 2.txt'
  836. ]);
  837. });
  838. });
  839. describe('Plugins', function() {
  840. var plugin;
  841. beforeEach(function() {
  842. plugin = {
  843. name: 'Some name',
  844. attach: function(obj) {
  845. obj.attached = true;
  846. },
  847. detach: function(obj) {
  848. obj.attached = false;
  849. }
  850. };
  851. OC.Plugins.register('OC.Test.SomeName', plugin);
  852. });
  853. it('attach plugin to object', function() {
  854. var obj = {something: true};
  855. OC.Plugins.attach('OC.Test.SomeName', obj);
  856. expect(obj.attached).toEqual(true);
  857. OC.Plugins.detach('OC.Test.SomeName', obj);
  858. expect(obj.attached).toEqual(false);
  859. });
  860. it('only call handler for target name', function() {
  861. var obj = {something: true};
  862. OC.Plugins.attach('OC.Test.SomeOtherName', obj);
  863. expect(obj.attached).not.toBeDefined();
  864. OC.Plugins.detach('OC.Test.SomeOtherName', obj);
  865. expect(obj.attached).not.toBeDefined();
  866. });
  867. });
  868. describe('Notifications', function() {
  869. var showSpy;
  870. var showHtmlSpy;
  871. var hideSpy;
  872. var clock;
  873. var getInnerText = function($node) {
  874. return $node.contents().filter(function(){
  875. return this.nodeType === 3;
  876. })[0].nodeValue;
  877. }
  878. beforeEach(function() {
  879. clock = sinon.useFakeTimers();
  880. showSpy = sinon.spy(OCP.Toast, 'message');
  881. hideSpy = sinon.spy(OC.Notification, 'hide');
  882. $('#testArea').append('<div id="content"></div>');
  883. });
  884. afterEach(function() {
  885. showSpy.restore();
  886. hideSpy.restore();
  887. // jump past animations
  888. clock.tick(10000);
  889. clock.restore();
  890. $('#testArea .toastify').remove();
  891. });
  892. describe('showTemporary', function() {
  893. it('shows a plain text notification with default timeout', function() {
  894. OC.Notification.showTemporary('My notification test');
  895. expect(showSpy.calledOnce).toEqual(true);
  896. expect(showSpy.firstCall.args[0]).toEqual('My notification test');
  897. //expect(showSpy.firstCall.args[1]).toEqual({isHTML: false, timeout: 7});
  898. var $row = $('#testArea .toastify');
  899. expect($row).toBeDefined();
  900. expect(getInnerText($row)).toEqual('My notification test');
  901. });
  902. it('shows a HTML notification with default timeout', function() {
  903. OC.Notification.showTemporary('<a>My notification test</a>', { isHTML: true });
  904. expect(showSpy.calledOnce).toEqual(true);
  905. expect(showSpy.firstCall.args[0]).toEqual('<a>My notification test</a>');
  906. expect(showSpy.firstCall.args[1].isHTML).toEqual(true)
  907. var $row = $('#testArea .toastify');
  908. expect($row).toBeDefined();
  909. expect(getInnerText($row)).toEqual('<a>My notification test</a>');
  910. });
  911. it('hides itself after 7 seconds', function() {
  912. OC.Notification.showTemporary('');
  913. var $row = $('#testArea .toastify');
  914. expect($row).toBeDefined();
  915. // travel in time +7000 milliseconds
  916. clock.tick(7500);
  917. $row = $('#testArea .toastify');
  918. expect($row.length).toEqual(0);
  919. });
  920. });
  921. describe('show', function() {
  922. it('hides itself after a given time', function() {
  923. OC.Notification.showTemporary('', {timeout: 10});
  924. var $row = $('#testArea .toastify');
  925. expect($row).toBeDefined();
  926. clock.tick(11500);
  927. $row = $('#testArea .toastify');
  928. expect($row.length).toEqual(0);
  929. });
  930. it('does not hide itself if no timeout given to show', function() {
  931. OC.Notification.show('');
  932. // travel in time +1000 seconds
  933. clock.tick(1000000);
  934. expect(hideSpy.notCalled).toEqual(true);
  935. });
  936. });
  937. it('cumulates several notifications', function() {
  938. var $row1 = OC.Notification.showTemporary('One');
  939. var $row2 = OC.Notification.showTemporary('Two', {timeout: 2});
  940. var $row3 = OC.Notification.showTemporary('Three');
  941. var $el = $('#testArea');
  942. var $rows = $el.find('.toastify');
  943. expect($rows.length).toEqual(3);
  944. expect($rows.eq(0).is($row3)).toEqual(true);
  945. expect($rows.eq(1).is($row2)).toEqual(true);
  946. expect($rows.eq(2).is($row1)).toEqual(true);
  947. clock.tick(3000);
  948. $rows = $el.find('.toastify');
  949. expect($rows.length).toEqual(2);
  950. expect($rows.eq(0).is($row3)).toEqual(true);
  951. expect($rows.eq(1).is($row1)).toEqual(true);
  952. });
  953. it('hides the given notification when calling hide with argument', function() {
  954. var $row1 = OC.Notification.show('One');
  955. var $row2 = OC.Notification.show('Two');
  956. var $el = $('#testArea');
  957. var $rows = $el.find('.toastify');
  958. expect($rows.length).toEqual(2);
  959. OC.Notification.hide($row2);
  960. clock.tick(3000);
  961. $rows = $el.find('.toastify');
  962. expect($rows.length).toEqual(1);
  963. expect($rows.eq(0).is($row1)).toEqual(true);
  964. });
  965. });
  966. describe('global ajax errors', function() {
  967. var reloadStub, ajaxErrorStub, clock;
  968. var notificationStub;
  969. var waitTimeMs = 6500;
  970. var oldCurrentUser;
  971. beforeEach(function() {
  972. oldCurrentUser = OC.currentUser;
  973. OC.currentUser = 'dummy';
  974. clock = sinon.useFakeTimers();
  975. reloadStub = sinon.stub(OC, 'reload');
  976. notificationStub = sinon.stub(OC.Notification, 'show');
  977. // unstub the error processing method
  978. ajaxErrorStub = OC._processAjaxError;
  979. ajaxErrorStub.restore();
  980. window.initCore();
  981. });
  982. afterEach(function() {
  983. OC.currentUser = oldCurrentUser;
  984. reloadStub.restore();
  985. notificationStub.restore();
  986. clock.restore();
  987. });
  988. it('reloads current page in case of auth error', function() {
  989. var dataProvider = [
  990. [200, false],
  991. [400, false],
  992. [0, false],
  993. [401, true],
  994. [302, true],
  995. [303, true],
  996. [307, true]
  997. ];
  998. for (var i = 0; i < dataProvider.length; i++) {
  999. var xhr = { status: dataProvider[i][0] };
  1000. var expectedCall = dataProvider[i][1];
  1001. reloadStub.reset();
  1002. OC._reloadCalled = false;
  1003. $(document).trigger(new $.Event('ajaxError'), xhr);
  1004. // trigger timers
  1005. clock.tick(waitTimeMs);
  1006. if (expectedCall) {
  1007. expect(reloadStub.calledOnce).toEqual(true);
  1008. } else {
  1009. expect(reloadStub.notCalled).toEqual(true);
  1010. }
  1011. }
  1012. });
  1013. it('reload only called once in case of auth error', function() {
  1014. var xhr = { status: 401 };
  1015. $(document).trigger(new $.Event('ajaxError'), xhr);
  1016. $(document).trigger(new $.Event('ajaxError'), xhr);
  1017. // trigger timers
  1018. clock.tick(waitTimeMs);
  1019. expect(reloadStub.calledOnce).toEqual(true);
  1020. });
  1021. it('does not reload the page if the user was navigating away', function() {
  1022. var xhr = { status: 0 };
  1023. OC._userIsNavigatingAway = true;
  1024. clock.tick(100);
  1025. $(document).trigger(new $.Event('ajaxError'), xhr);
  1026. clock.tick(waitTimeMs);
  1027. expect(reloadStub.notCalled).toEqual(true);
  1028. });
  1029. it('displays notification', function() {
  1030. var xhr = { status: 401 };
  1031. notificationUpdateStub = sinon.stub(OC.Notification, 'showUpdate');
  1032. $(document).trigger(new $.Event('ajaxError'), xhr);
  1033. clock.tick(waitTimeMs);
  1034. expect(notificationUpdateStub.notCalled).toEqual(false);
  1035. });
  1036. it('shows a temporary notification if the connection is lost', function() {
  1037. var xhr = { status: 0 };
  1038. spyOn(OC, '_ajaxConnectionLostHandler');
  1039. $(document).trigger(new $.Event('ajaxError'), xhr);
  1040. clock.tick(101);
  1041. expect(OC._ajaxConnectionLostHandler.calls.count()).toBe(1);
  1042. });
  1043. });
  1044. describe('Snapper', function() {
  1045. var snapConstructorStub;
  1046. var snapperStub;
  1047. var clock;
  1048. beforeEach(function() {
  1049. snapConstructorStub = sinon.stub(window, 'Snap');
  1050. snapperStub = {};
  1051. snapperStub.enable = sinon.stub();
  1052. snapperStub.disable = sinon.stub();
  1053. snapperStub.close = sinon.stub();
  1054. snapConstructorStub.returns(snapperStub);
  1055. clock = sinon.useFakeTimers();
  1056. // _.now could have been set to Date.now before Sinon replaced it
  1057. // with a fake version, so _.now must be stubbed to ensure that the
  1058. // fake Date.now will be called instead of the original one.
  1059. _.now = sinon.stub(_, 'now').callsFake(function() {
  1060. return new Date().getTime();
  1061. });
  1062. $('#testArea').append('<div id="app-navigation">The navigation bar</div><div id="app-content">Content</div>');
  1063. });
  1064. afterEach(function() {
  1065. snapConstructorStub.restore();
  1066. clock.restore();
  1067. _.now.restore();
  1068. // Remove the event handler for the resize event added to the window
  1069. // due to calling window.initCore() when there is an #app-navigation
  1070. // element.
  1071. $(window).off('resize');
  1072. viewport.reset();
  1073. });
  1074. it('is enabled on a narrow screen', function() {
  1075. viewport.set(480);
  1076. window.initCore();
  1077. expect(snapConstructorStub.calledOnce).toBe(true);
  1078. expect(snapperStub.enable.calledOnce).toBe(true);
  1079. expect(snapperStub.disable.called).toBe(false);
  1080. });
  1081. it('is disabled when disallowing the gesture on a narrow screen', function() {
  1082. viewport.set(480);
  1083. window.initCore();
  1084. expect(snapperStub.enable.calledOnce).toBe(true);
  1085. expect(snapperStub.disable.called).toBe(false);
  1086. expect(snapperStub.close.called).toBe(false);
  1087. OC.disallowNavigationBarSlideGesture();
  1088. expect(snapperStub.enable.calledOnce).toBe(true);
  1089. expect(snapperStub.disable.calledOnce).toBe(true);
  1090. expect(snapperStub.disable.alwaysCalledWithExactly(true)).toBe(true);
  1091. expect(snapperStub.close.called).toBe(false);
  1092. });
  1093. it('is not disabled again when disallowing the gesture twice on a narrow screen', function() {
  1094. viewport.set(480);
  1095. window.initCore();
  1096. expect(snapperStub.enable.calledOnce).toBe(true);
  1097. expect(snapperStub.disable.called).toBe(false);
  1098. expect(snapperStub.close.called).toBe(false);
  1099. OC.disallowNavigationBarSlideGesture();
  1100. expect(snapperStub.enable.calledOnce).toBe(true);
  1101. expect(snapperStub.disable.calledOnce).toBe(true);
  1102. expect(snapperStub.disable.alwaysCalledWithExactly(true)).toBe(true);
  1103. expect(snapperStub.close.called).toBe(false);
  1104. OC.disallowNavigationBarSlideGesture();
  1105. expect(snapperStub.enable.calledOnce).toBe(true);
  1106. expect(snapperStub.disable.calledOnce).toBe(true);
  1107. expect(snapperStub.close.called).toBe(false);
  1108. });
  1109. it('is enabled when allowing the gesture after disallowing it on a narrow screen', function() {
  1110. viewport.set(480);
  1111. window.initCore();
  1112. expect(snapperStub.enable.calledOnce).toBe(true);
  1113. expect(snapperStub.disable.called).toBe(false);
  1114. expect(snapperStub.close.called).toBe(false);
  1115. OC.disallowNavigationBarSlideGesture();
  1116. expect(snapperStub.enable.calledOnce).toBe(true);
  1117. expect(snapperStub.disable.calledOnce).toBe(true);
  1118. expect(snapperStub.disable.alwaysCalledWithExactly(true)).toBe(true);
  1119. expect(snapperStub.close.called).toBe(false);
  1120. OC.allowNavigationBarSlideGesture();
  1121. expect(snapperStub.enable.calledTwice).toBe(true);
  1122. expect(snapperStub.disable.calledOnce).toBe(true);
  1123. expect(snapperStub.close.called).toBe(false);
  1124. });
  1125. it('is not enabled again when allowing the gesture twice after disallowing it on a narrow screen', function() {
  1126. viewport.set(480);
  1127. window.initCore();
  1128. expect(snapperStub.enable.calledOnce).toBe(true);
  1129. expect(snapperStub.disable.called).toBe(false);
  1130. expect(snapperStub.close.called).toBe(false);
  1131. OC.disallowNavigationBarSlideGesture();
  1132. expect(snapperStub.enable.calledOnce).toBe(true);
  1133. expect(snapperStub.disable.calledOnce).toBe(true);
  1134. expect(snapperStub.disable.alwaysCalledWithExactly(true)).toBe(true);
  1135. expect(snapperStub.close.called).toBe(false);
  1136. OC.allowNavigationBarSlideGesture();
  1137. expect(snapperStub.enable.calledTwice).toBe(true);
  1138. expect(snapperStub.disable.calledOnce).toBe(true);
  1139. expect(snapperStub.close.called).toBe(false);
  1140. OC.allowNavigationBarSlideGesture();
  1141. expect(snapperStub.enable.calledTwice).toBe(true);
  1142. expect(snapperStub.disable.calledOnce).toBe(true);
  1143. expect(snapperStub.close.called).toBe(false);
  1144. });
  1145. it('is disabled on a wide screen', function() {
  1146. viewport.set(1280);
  1147. window.initCore();
  1148. expect(snapConstructorStub.calledOnce).toBe(true);
  1149. expect(snapperStub.enable.called).toBe(false);
  1150. expect(snapperStub.disable.calledOnce).toBe(true);
  1151. });
  1152. it('is not disabled again when disallowing the gesture on a wide screen', function() {
  1153. viewport.set(1280);
  1154. window.initCore();
  1155. expect(snapperStub.enable.called).toBe(false);
  1156. expect(snapperStub.disable.calledOnce).toBe(true);
  1157. expect(snapperStub.close.calledOnce).toBe(true);
  1158. OC.disallowNavigationBarSlideGesture();
  1159. expect(snapperStub.enable.called).toBe(false);
  1160. expect(snapperStub.disable.calledOnce).toBe(true);
  1161. expect(snapperStub.close.calledOnce).toBe(true);
  1162. });
  1163. it('is not enabled when allowing the gesture after disallowing it on a wide screen', function() {
  1164. viewport.set(1280);
  1165. window.initCore();
  1166. expect(snapperStub.enable.called).toBe(false);
  1167. expect(snapperStub.disable.calledOnce).toBe(true);
  1168. expect(snapperStub.close.calledOnce).toBe(true);
  1169. OC.disallowNavigationBarSlideGesture();
  1170. expect(snapperStub.enable.called).toBe(false);
  1171. expect(snapperStub.disable.calledOnce).toBe(true);
  1172. expect(snapperStub.close.calledOnce).toBe(true);
  1173. OC.allowNavigationBarSlideGesture();
  1174. expect(snapperStub.enable.called).toBe(false);
  1175. expect(snapperStub.disable.calledOnce).toBe(true);
  1176. expect(snapperStub.close.calledOnce).toBe(true);
  1177. });
  1178. it('is enabled when resizing to a narrow screen', function() {
  1179. viewport.set(1280);
  1180. window.initCore();
  1181. expect(snapperStub.enable.called).toBe(false);
  1182. expect(snapperStub.disable.calledOnce).toBe(true);
  1183. viewport.set(480);
  1184. // Setting the viewport width does not automatically trigger a
  1185. // resize.
  1186. $(window).resize();
  1187. // The resize handler is debounced to be executed a few milliseconds
  1188. // after the resize event.
  1189. clock.tick(1000);
  1190. expect(snapperStub.enable.calledOnce).toBe(true);
  1191. expect(snapperStub.disable.calledOnce).toBe(true);
  1192. });
  1193. it('is not enabled when resizing to a narrow screen after disallowing the gesture', function() {
  1194. viewport.set(1280);
  1195. window.initCore();
  1196. expect(snapperStub.enable.called).toBe(false);
  1197. expect(snapperStub.disable.calledOnce).toBe(true);
  1198. OC.disallowNavigationBarSlideGesture();
  1199. expect(snapperStub.enable.called).toBe(false);
  1200. expect(snapperStub.disable.calledOnce).toBe(true);
  1201. viewport.set(480);
  1202. // Setting the viewport width does not automatically trigger a
  1203. // resize.
  1204. $(window).resize();
  1205. // The resize handler is debounced to be executed a few milliseconds
  1206. // after the resize event.
  1207. clock.tick(1000);
  1208. expect(snapperStub.enable.called).toBe(false);
  1209. expect(snapperStub.disable.calledOnce).toBe(true);
  1210. });
  1211. it('is enabled when resizing to a narrow screen after disallowing the gesture and allowing it', function() {
  1212. viewport.set(1280);
  1213. window.initCore();
  1214. expect(snapperStub.enable.called).toBe(false);
  1215. expect(snapperStub.disable.calledOnce).toBe(true);
  1216. OC.disallowNavigationBarSlideGesture();
  1217. expect(snapperStub.enable.called).toBe(false);
  1218. expect(snapperStub.disable.calledOnce).toBe(true);
  1219. OC.allowNavigationBarSlideGesture();
  1220. expect(snapperStub.enable.called).toBe(false);
  1221. expect(snapperStub.disable.calledOnce).toBe(true);
  1222. viewport.set(480);
  1223. // Setting the viewport width does not automatically trigger a
  1224. // resize.
  1225. $(window).resize();
  1226. // The resize handler is debounced to be executed a few milliseconds
  1227. // after the resize event.
  1228. clock.tick(1000);
  1229. expect(snapperStub.enable.calledOnce).toBe(true);
  1230. expect(snapperStub.disable.calledOnce).toBe(true);
  1231. });
  1232. it('is enabled when allowing the gesture after disallowing it and resizing to a narrow screen', function() {
  1233. viewport.set(1280);
  1234. window.initCore();
  1235. expect(snapperStub.enable.called).toBe(false);
  1236. expect(snapperStub.disable.calledOnce).toBe(true);
  1237. OC.disallowNavigationBarSlideGesture();
  1238. expect(snapperStub.enable.called).toBe(false);
  1239. expect(snapperStub.disable.calledOnce).toBe(true);
  1240. viewport.set(480);
  1241. // Setting the viewport width does not automatically trigger a
  1242. // resize.
  1243. $(window).resize();
  1244. // The resize handler is debounced to be executed a few milliseconds
  1245. // after the resize event.
  1246. clock.tick(1000);
  1247. expect(snapperStub.enable.called).toBe(false);
  1248. expect(snapperStub.disable.calledOnce).toBe(true);
  1249. OC.allowNavigationBarSlideGesture();
  1250. expect(snapperStub.enable.calledOnce).toBe(true);
  1251. expect(snapperStub.disable.calledOnce).toBe(true);
  1252. });
  1253. it('is disabled when disallowing the gesture after disallowing it, resizing to a narrow screen and allowing it', function() {
  1254. viewport.set(1280);
  1255. window.initCore();
  1256. expect(snapperStub.enable.called).toBe(false);
  1257. expect(snapperStub.disable.calledOnce).toBe(true);
  1258. OC.disallowNavigationBarSlideGesture();
  1259. expect(snapperStub.enable.called).toBe(false);
  1260. expect(snapperStub.disable.calledOnce).toBe(true);
  1261. viewport.set(480);
  1262. // Setting the viewport width does not automatically trigger a
  1263. // resize.
  1264. $(window).resize();
  1265. // The resize handler is debounced to be executed a few milliseconds
  1266. // after the resize event.
  1267. clock.tick(1000);
  1268. expect(snapperStub.enable.called).toBe(false);
  1269. expect(snapperStub.disable.calledOnce).toBe(true);
  1270. OC.allowNavigationBarSlideGesture();
  1271. expect(snapperStub.enable.calledOnce).toBe(true);
  1272. expect(snapperStub.disable.calledOnce).toBe(true);
  1273. OC.disallowNavigationBarSlideGesture();
  1274. expect(snapperStub.enable.calledOnce).toBe(true);
  1275. expect(snapperStub.disable.calledTwice).toBe(true);
  1276. expect(snapperStub.disable.getCall(1).calledWithExactly(true)).toBe(true);
  1277. });
  1278. it('is disabled when resizing to a wide screen', function() {
  1279. viewport.set(480);
  1280. window.initCore();
  1281. expect(snapperStub.enable.calledOnce).toBe(true);
  1282. expect(snapperStub.disable.called).toBe(false);
  1283. expect(snapperStub.close.called).toBe(false);
  1284. viewport.set(1280);
  1285. // Setting the viewport width does not automatically trigger a
  1286. // resize.
  1287. $(window).resize();
  1288. // The resize handler is debounced to be executed a few milliseconds
  1289. // after the resize event.
  1290. clock.tick(1000);
  1291. expect(snapperStub.enable.calledOnce).toBe(true);
  1292. expect(snapperStub.disable.calledOnce).toBe(true);
  1293. expect(snapperStub.close.calledOnce).toBe(true);
  1294. });
  1295. it('is not disabled again when disallowing the gesture after resizing to a wide screen', function() {
  1296. viewport.set(480);
  1297. window.initCore();
  1298. expect(snapperStub.enable.calledOnce).toBe(true);
  1299. expect(snapperStub.disable.called).toBe(false);
  1300. expect(snapperStub.close.called).toBe(false);
  1301. viewport.set(1280);
  1302. // Setting the viewport width does not automatically trigger a
  1303. // resize.
  1304. $(window).resize();
  1305. // The resize handler is debounced to be executed a few milliseconds
  1306. // after the resize event.
  1307. clock.tick(1000);
  1308. expect(snapperStub.enable.calledOnce).toBe(true);
  1309. expect(snapperStub.disable.calledOnce).toBe(true);
  1310. expect(snapperStub.close.calledOnce).toBe(true);
  1311. OC.disallowNavigationBarSlideGesture();
  1312. expect(snapperStub.enable.calledOnce).toBe(true);
  1313. expect(snapperStub.disable.calledOnce).toBe(true);
  1314. expect(snapperStub.close.calledOnce).toBe(true);
  1315. });
  1316. it('is not enabled when allowing the gesture after disallowing it, resizing to a narrow screen and resizing to a wide screen', function() {
  1317. viewport.set(1280);
  1318. window.initCore();
  1319. expect(snapperStub.enable.called).toBe(false);
  1320. expect(snapperStub.disable.calledOnce).toBe(true);
  1321. expect(snapperStub.close.calledOnce).toBe(true);
  1322. OC.disallowNavigationBarSlideGesture();
  1323. expect(snapperStub.enable.called).toBe(false);
  1324. expect(snapperStub.disable.calledOnce).toBe(true);
  1325. expect(snapperStub.close.calledOnce).toBe(true);
  1326. viewport.set(480);
  1327. // Setting the viewport width does not automatically trigger a
  1328. // resize.
  1329. $(window).resize();
  1330. // The resize handler is debounced to be executed a few milliseconds
  1331. // after the resize event.
  1332. clock.tick(1000);
  1333. expect(snapperStub.enable.called).toBe(false);
  1334. expect(snapperStub.disable.calledOnce).toBe(true);
  1335. expect(snapperStub.close.calledOnce).toBe(true);
  1336. viewport.set(1280);
  1337. $(window).resize();
  1338. clock.tick(1000);
  1339. expect(snapperStub.enable.called).toBe(false);
  1340. expect(snapperStub.disable.calledTwice).toBe(true);
  1341. expect(snapperStub.close.calledTwice).toBe(true);
  1342. OC.allowNavigationBarSlideGesture();
  1343. expect(snapperStub.enable.called).toBe(false);
  1344. expect(snapperStub.disable.calledTwice).toBe(true);
  1345. expect(snapperStub.close.calledTwice).toBe(true);
  1346. });
  1347. });
  1348. describe('Requires password confirmation', function () {
  1349. var stubMomentNow;
  1350. var stubJsPageLoadTime;
  1351. afterEach(function () {
  1352. delete window.nc_pageLoad;
  1353. delete window.nc_lastLogin;
  1354. delete window.backendAllowsPasswordConfirmation;
  1355. stubMomentNow.restore();
  1356. stubJsPageLoadTime.restore();
  1357. });
  1358. it('should not show the password confirmation dialog when server time is earlier than local time', function () {
  1359. // add server variables
  1360. window.nc_pageLoad = parseInt(new Date(2018, 0, 3, 1, 15, 0).getTime() / 1000);
  1361. window.nc_lastLogin = parseInt(new Date(2018, 0, 3, 1, 0, 0).getTime() / 1000);
  1362. window.backendAllowsPasswordConfirmation = true;
  1363. stubJsPageLoadTime = sinon.stub(OC.PasswordConfirmation, 'pageLoadTime').value(new Date(2018, 0, 3, 12, 15, 0).getTime());
  1364. stubMomentNow = sinon.stub(moment, 'now').returns(new Date(2018, 0, 3, 12, 20, 0).getTime());
  1365. expect(OC.PasswordConfirmation.requiresPasswordConfirmation()).toBeFalsy();
  1366. });
  1367. it('should show the password confirmation dialog when server time is earlier than local time', function () {
  1368. // add server variables
  1369. window.nc_pageLoad = parseInt(new Date(2018, 0, 3, 1, 15, 0).getTime() / 1000);
  1370. window.nc_lastLogin = parseInt(new Date(2018, 0, 3, 1, 0, 0).getTime() / 1000);
  1371. window.backendAllowsPasswordConfirmation = true;
  1372. stubJsPageLoadTime = sinon.stub(OC.PasswordConfirmation, 'pageLoadTime').value(new Date(2018, 0, 3, 12, 15, 0).getTime());
  1373. stubMomentNow = sinon.stub(moment, 'now').returns(new Date(2018, 0, 3, 12, 31, 0).getTime());
  1374. expect(OC.PasswordConfirmation.requiresPasswordConfirmation()).toBeTruthy();
  1375. });
  1376. it('should not show the password confirmation dialog when server time is later than local time', function () {
  1377. // add server variables
  1378. window.nc_pageLoad = parseInt(new Date(2018, 0, 3, 23, 15, 0).getTime() / 1000);
  1379. window.nc_lastLogin = parseInt(new Date(2018, 0, 3, 23, 0, 0).getTime() / 1000);
  1380. window.backendAllowsPasswordConfirmation = true;
  1381. stubJsPageLoadTime = sinon.stub(OC.PasswordConfirmation, 'pageLoadTime').value(new Date(2018, 0, 3, 12, 15, 0).getTime());
  1382. stubMomentNow = sinon.stub(moment, 'now').returns(new Date(2018, 0, 3, 12, 20, 0).getTime());
  1383. expect(OC.PasswordConfirmation.requiresPasswordConfirmation()).toBeFalsy();
  1384. });
  1385. it('should show the password confirmation dialog when server time is later than local time', function () {
  1386. // add server variables
  1387. window.nc_pageLoad = parseInt(new Date(2018, 0, 3, 23, 15, 0).getTime() / 1000);
  1388. window.nc_lastLogin = parseInt(new Date(2018, 0, 3, 23, 0, 0).getTime() / 1000);
  1389. window.backendAllowsPasswordConfirmation = true;
  1390. stubJsPageLoadTime = sinon.stub(OC.PasswordConfirmation, 'pageLoadTime').value(new Date(2018, 0, 3, 12, 15, 0).getTime());
  1391. stubMomentNow = sinon.stub(moment, 'now').returns(new Date(2018, 0, 3, 12, 31, 0).getTime());
  1392. expect(OC.PasswordConfirmation.requiresPasswordConfirmation()).toBeTruthy();
  1393. });
  1394. });
  1395. });