Utils.test.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  1. const utils = require('./Utils')
  2. const sampleImageDataURI =
  3. 'data:image/jpeg;base64,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'
  4. describe('core/utils', () => {
  5. describe('generateFileID', () => {
  6. it('should take the filename object and produce a lowercase file id made up of uppy- prefix, file name (cleaned up to be lowercase, letters and numbers only), type, size and lastModified date', () => {
  7. const fileObj = {
  8. name: 'fOo0Fi@£$.jpg',
  9. type: 'image/jpeg',
  10. data: {
  11. lastModified: 1498510508000,
  12. size: 2271173
  13. }
  14. }
  15. expect(utils.generateFileID(fileObj)).toEqual(
  16. 'uppy-foo0fijpg-image/jpeg-2271173-1498510508000'
  17. )
  18. })
  19. })
  20. describe('toArray', () => {
  21. it('should convert a array-like object into an array', () => {
  22. const obj = {
  23. '0': 'zero',
  24. '1': 'one',
  25. '2': 'two',
  26. '3': 'three',
  27. '4': 'four',
  28. length: 5
  29. }
  30. expect(utils.toArray(obj)).toEqual([
  31. 'zero',
  32. 'one',
  33. 'two',
  34. 'three',
  35. 'four'
  36. ])
  37. })
  38. })
  39. describe('runPromiseSequence', () => {
  40. it('should run an array of promise-returning functions in sequence', () => {
  41. const promiseFn1 = jest.fn().mockReturnValue(Promise.resolve)
  42. const promiseFn2 = jest.fn().mockReturnValue(Promise.resolve)
  43. const promiseFn3 = jest.fn().mockReturnValue(Promise.resolve)
  44. return utils
  45. .runPromiseSequence([promiseFn1, promiseFn2, promiseFn3])
  46. .then(() => {
  47. expect(promiseFn1.mock.calls.length).toEqual(1)
  48. expect(promiseFn2.mock.calls.length).toEqual(1)
  49. expect(promiseFn3.mock.calls.length).toEqual(1)
  50. })
  51. })
  52. })
  53. describe('isTouchDevice', () => {
  54. const RealTouchStart = global.window.ontouchstart
  55. const RealMaxTouchPoints = global.navigator.maxTouchPoints
  56. beforeEach(() => {
  57. global.window.ontouchstart = true
  58. global.navigator.maxTouchPoints = 1
  59. })
  60. afterEach(() => {
  61. global.navigator.maxTouchPoints = RealMaxTouchPoints
  62. global.window.ontouchstart = RealTouchStart
  63. })
  64. xit("should return true if it's a touch device", () => {
  65. expect(utils.isTouchDevice()).toEqual(true)
  66. delete global.window.ontouchstart
  67. global.navigator.maxTouchPoints = false
  68. expect(utils.isTouchDevice()).toEqual(false)
  69. })
  70. })
  71. describe('getFileNameAndExtension', () => {
  72. it('should return the filename and extension as an array', () => {
  73. expect(utils.getFileNameAndExtension('fsdfjodsuf23rfw.jpg')).toEqual({
  74. name: 'fsdfjodsuf23rfw',
  75. extension: 'jpg'
  76. })
  77. })
  78. it('should handle invalid filenames', () => {
  79. expect(utils.getFileNameAndExtension('fsdfjodsuf23rfw')).toEqual({
  80. name: 'fsdfjodsuf23rfw',
  81. extension: undefined
  82. })
  83. })
  84. })
  85. describe('truncateString', () => {
  86. it('should truncate the string by the specified amount', () => {
  87. expect(utils.truncateString('abcdefghijkl', 10)).toEqual('abcde...jkl')
  88. expect(utils.truncateString('abcdefghijkl', 9)).toEqual('abcd...jkl')
  89. expect(utils.truncateString('abcdefghijkl', 8)).toEqual('abcd...kl')
  90. expect(utils.truncateString('abcdefghijkl', 7)).toEqual('abc...kl')
  91. expect(utils.truncateString('abcdefghijkl', 6)).toEqual('abc...kl')
  92. expect(utils.truncateString('abcdefghijkl', 5)).toEqual('ab...kl')
  93. expect(utils.truncateString('abcdefghijkl', 4)).toEqual('ab...l')
  94. expect(utils.truncateString('abcdefghijkl', 3)).toEqual('a...l')
  95. expect(utils.truncateString('abcdefghijkl', 2)).toEqual('a...l')
  96. expect(utils.truncateString('abcdefghijkl', 1)).toEqual('...l')
  97. })
  98. })
  99. describe('secondsToTime', () => {
  100. expect(utils.secondsToTime(60)).toEqual({
  101. hours: 0,
  102. minutes: 1,
  103. seconds: 0
  104. })
  105. expect(utils.secondsToTime(123)).toEqual({
  106. hours: 0,
  107. minutes: 2,
  108. seconds: 3
  109. })
  110. expect(utils.secondsToTime(1060)).toEqual({
  111. hours: 0,
  112. minutes: 17,
  113. seconds: 40
  114. })
  115. expect(utils.secondsToTime(123453460)).toEqual({
  116. hours: 20,
  117. minutes: 37,
  118. seconds: 40
  119. })
  120. })
  121. describe('getFileTypeExtension', () => {
  122. it('should return the filetype based on the specified mime type', () => {
  123. expect(utils.getFileTypeExtension('video/ogg')).toEqual('ogv')
  124. expect(utils.getFileTypeExtension('audio/ogg')).toEqual('ogg')
  125. expect(utils.getFileTypeExtension('video/webm')).toEqual('webm')
  126. expect(utils.getFileTypeExtension('audio/webm')).toEqual('webm')
  127. expect(utils.getFileTypeExtension('video/mp4')).toEqual('mp4')
  128. expect(utils.getFileTypeExtension('audio/mp3')).toEqual('mp3')
  129. expect(utils.getFileTypeExtension('foo/bar')).toEqual(null)
  130. })
  131. })
  132. describe('getFileType', () => {
  133. it('should trust the filetype if the file comes from a remote source', () => {
  134. const file = {
  135. isRemote: true,
  136. type: 'audio/webm',
  137. name: 'foo.webm'
  138. }
  139. expect(utils.getFileType(file)).toEqual('audio/webm')
  140. })
  141. it('should determine the filetype from the mimetype', () => {
  142. const file = {
  143. type: 'audio/webm',
  144. name: 'foo.webm',
  145. data: 'sdfsdfhq9efbicw'
  146. }
  147. expect(utils.getFileType(file)).toEqual('audio/webm')
  148. })
  149. it('should determine the filetype from the extension', () => {
  150. const fileMP3 = {
  151. name: 'foo.mp3',
  152. data: 'sdfsfhfh329fhwihs'
  153. }
  154. const fileYAML = {
  155. name: 'bar.yaml',
  156. data: 'sdfsfhfh329fhwihs'
  157. }
  158. const fileMKV = {
  159. name: 'bar.mkv',
  160. data: 'sdfsfhfh329fhwihs'
  161. }
  162. expect(utils.getFileType(fileMP3)).toEqual('audio/mp3')
  163. expect(utils.getFileType(fileYAML)).toEqual('text/yaml')
  164. expect(utils.getFileType(fileMKV)).toEqual('video/x-matroska')
  165. })
  166. it('should fail gracefully if unable to detect', () => {
  167. const file = {
  168. name: 'foobar',
  169. data: 'sdfsfhfh329fhwihs'
  170. }
  171. expect(utils.getFileType(file)).toEqual(null)
  172. })
  173. })
  174. describe('getArrayBuffer', () => {
  175. beforeEach(() => {
  176. global.FileReader = class FileReader {
  177. addEventListener (e, cb) {
  178. if (e === 'load') {
  179. this.loadCb = cb
  180. }
  181. if (e === 'error') {
  182. this.errorCb = cb
  183. }
  184. }
  185. readAsArrayBuffer (chunk) {
  186. this.loadCb({ target: { result: new ArrayBuffer(8) } })
  187. }
  188. }
  189. })
  190. afterEach(() => {
  191. global.FileReader = undefined
  192. })
  193. it('should return a promise that resolves with the specified chunk', () => {
  194. return utils.getArrayBuffer('abcde').then(buffer => {
  195. expect(typeof buffer).toEqual('object')
  196. expect(buffer.byteLength).toEqual(8)
  197. })
  198. })
  199. })
  200. describe('isPreviewSupported', () => {
  201. it('should return true for any filetypes that browsers can preview', () => {
  202. const supported = ['image/jpeg', 'image/gif', 'image/png', 'image/svg', 'image/svg+xml', 'image/bmp']
  203. supported.forEach(ext => {
  204. expect(utils.isPreviewSupported(ext)).toEqual(true)
  205. })
  206. expect(utils.isPreviewSupported('foo')).toEqual(false)
  207. })
  208. })
  209. describe('isObjectURL', () => {
  210. it('should return true if the specified url is an object url', () => {
  211. expect(utils.isObjectURL('blob:abc123')).toEqual(true)
  212. expect(utils.isObjectURL('kblob:abc123')).toEqual(false)
  213. expect(utils.isObjectURL('blob-abc123')).toEqual(false)
  214. expect(utils.isObjectURL('abc123')).toEqual(false)
  215. })
  216. })
  217. describe('dataURItoBlob', () => {
  218. it('should convert a data uri to a blob', () => {
  219. const blob = utils.dataURItoBlob(sampleImageDataURI, {})
  220. expect(blob instanceof Blob).toEqual(true)
  221. expect(blob.size).toEqual(9348)
  222. expect(blob.type).toEqual('image/jpeg')
  223. })
  224. })
  225. describe('dataURItoFile', () => {
  226. it('should convert a data uri to a file', () => {
  227. const file = utils.dataURItoFile(sampleImageDataURI, { name: 'foo.jpg' })
  228. expect(file instanceof File).toEqual(true)
  229. expect(file.size).toEqual(9348)
  230. expect(file.type).toEqual('image/jpeg')
  231. expect(file.name).toEqual('foo.jpg')
  232. })
  233. })
  234. describe('getSpeed', () => {
  235. it('should calculate the speed given a fileProgress object', () => {
  236. const dateNow = new Date()
  237. const date5SecondsAgo = new Date(dateNow.getTime() - 5 * 1000)
  238. const fileProgress = {
  239. bytesUploaded: 1024,
  240. uploadStarted: date5SecondsAgo
  241. }
  242. expect(Math.round(utils.getSpeed(fileProgress))).toEqual(Math.round(205))
  243. })
  244. })
  245. describe('getBytesRemaining', () => {
  246. it('should calculate the bytes remaining given a fileProgress object', () => {
  247. const fileProgress = {
  248. bytesUploaded: 1024,
  249. bytesTotal: 3096
  250. }
  251. expect(utils.getBytesRemaining(fileProgress)).toEqual(2072)
  252. })
  253. })
  254. describe('getETA', () => {
  255. it('should get the ETA remaining based on a fileProgress object', () => {
  256. const dateNow = new Date()
  257. const date5SecondsAgo = new Date(dateNow.getTime() - 5 * 1000)
  258. const fileProgress = {
  259. bytesUploaded: 1024,
  260. bytesTotal: 3096,
  261. uploadStarted: date5SecondsAgo
  262. }
  263. expect(utils.getETA(fileProgress)).toEqual(10.1)
  264. })
  265. })
  266. describe('prettyETA', () => {
  267. it('should convert the specified number of seconds to a pretty ETA', () => {
  268. expect(utils.prettyETA(0)).toEqual('0s')
  269. expect(utils.prettyETA(1.2)).toEqual('1s')
  270. expect(utils.prettyETA(1)).toEqual('1s')
  271. expect(utils.prettyETA(103)).toEqual('1m 43s')
  272. expect(utils.prettyETA(1034.9)).toEqual('17m 14s')
  273. expect(utils.prettyETA(103984.1)).toEqual('4h 53m 04s')
  274. })
  275. })
  276. describe('copyToClipboard', () => {
  277. xit('should copy the specified text to the clipboard', () => {})
  278. })
  279. describe('getSocketHost', () => {
  280. it('should get the host from the specified url', () => {
  281. expect(
  282. utils.getSocketHost('https://foo.bar/a/b/cd?e=fghi&l=k&m=n')
  283. ).toEqual('ws://foo.bar/a/b/cd?e=fghi&l=k&m=n')
  284. })
  285. })
  286. describe('settle', () => {
  287. it('should resolve even if all input promises reject', () => {
  288. return expect(
  289. utils.settle([
  290. Promise.reject(new Error('oops')),
  291. Promise.reject(new Error('this went wrong'))
  292. ])
  293. ).resolves.toMatchObject({
  294. successful: [],
  295. failed: [ new Error('oops'), new Error('this went wrong') ]
  296. })
  297. })
  298. it('should resolve with an object if some input promises resolve', () => {
  299. return expect(
  300. utils.settle([
  301. Promise.reject(new Error('rejected')),
  302. Promise.resolve('resolved'),
  303. Promise.resolve('also-resolved')
  304. ])
  305. ).resolves.toMatchObject({
  306. successful: ['resolved', 'also-resolved'],
  307. failed: [new Error('rejected')]
  308. })
  309. })
  310. })
  311. describe('limitPromises', () => {
  312. let pending = 0
  313. function fn () {
  314. pending++
  315. return new Promise((resolve) => setTimeout(resolve, 10))
  316. .then(() => pending--)
  317. }
  318. it('should run at most N promises at the same time', () => {
  319. const limit = utils.limitPromises(4)
  320. const fn2 = limit(fn)
  321. const result = Promise.all([
  322. fn2(), fn2(), fn2(), fn2(),
  323. fn2(), fn2(), fn2(), fn2(),
  324. fn2(), fn2()
  325. ])
  326. expect(pending).toBe(4)
  327. setTimeout(() => {
  328. expect(pending).toBe(4)
  329. }, 10)
  330. return result.then(() => {
  331. expect(pending).toBe(0)
  332. })
  333. })
  334. it('should accept Infinity as limit', () => {
  335. const limit = utils.limitPromises(Infinity)
  336. const fn2 = limit(fn)
  337. const result = Promise.all([
  338. fn2(), fn2(), fn2(), fn2(),
  339. fn2(), fn2(), fn2(), fn2(),
  340. fn2(), fn2()
  341. ])
  342. expect(pending).toBe(10)
  343. return result.then(() => {
  344. expect(pending).toBe(0)
  345. })
  346. })
  347. })
  348. })