Utils.test.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. import utils from './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. beforeEach(() => {
  134. global.FileReader = class FileReader {
  135. addEventListener (e, cb) {
  136. if (e === 'load') {
  137. this.loadCb = cb
  138. }
  139. if (e === 'error') {
  140. this.errorCb = cb
  141. }
  142. }
  143. readAsArrayBuffer (chunk) {
  144. this.loadCb({ target: { result: new ArrayBuffer(8) } })
  145. }
  146. }
  147. })
  148. afterEach(() => {
  149. global.FileReader = undefined
  150. })
  151. it('should trust the filetype if the file comes from a remote source', () => {
  152. const file = {
  153. isRemote: true,
  154. type: 'audio/webm',
  155. name: 'foo.webm'
  156. }
  157. return utils.getFileType(file).then(r => {
  158. expect(r).toEqual('audio/webm')
  159. })
  160. })
  161. it('should determine the filetype from the mimetype', () => {
  162. const file = {
  163. type: 'audio/webm',
  164. name: 'foo.webm',
  165. data: 'sdfsdfhq9efbicw'
  166. }
  167. return utils.getFileType(file).then(r => {
  168. expect(r).toEqual('audio/webm')
  169. })
  170. })
  171. it('should determine the filetype from the extension', () => {
  172. const file = {
  173. name: 'foo.mp3',
  174. data: 'sdfsfhfh329fhwihs'
  175. }
  176. return utils.getFileType(file).then(r => {
  177. expect(r).toEqual('audio/mp3')
  178. })
  179. })
  180. it('should fail gracefully if unable to detect', () => {
  181. const file = {
  182. name: 'foobar',
  183. data: 'sdfsfhfh329fhwihs'
  184. }
  185. return utils.getFileType(file).then(r => {
  186. expect(r).toEqual(null)
  187. })
  188. })
  189. })
  190. describe('getArrayBuffer', () => {
  191. beforeEach(() => {
  192. global.FileReader = class FileReader {
  193. addEventListener (e, cb) {
  194. if (e === 'load') {
  195. this.loadCb = cb
  196. }
  197. if (e === 'error') {
  198. this.errorCb = cb
  199. }
  200. }
  201. readAsArrayBuffer (chunk) {
  202. this.loadCb({ target: { result: new ArrayBuffer(8) } })
  203. }
  204. }
  205. })
  206. afterEach(() => {
  207. global.FileReader = undefined
  208. })
  209. it('should return a promise that resolves with the specified chunk', () => {
  210. return utils.getArrayBuffer('abcde').then(buffer => {
  211. expect(typeof buffer).toEqual('object')
  212. expect(buffer.byteLength).toEqual(8)
  213. })
  214. })
  215. })
  216. describe('isPreviewSupported', () => {
  217. it('should return true for any filetypes that browsers can preview', () => {
  218. const supported = ['image/jpeg', 'image/gif', 'image/png', 'image/svg', 'image/svg+xml', 'image/bmp']
  219. supported.forEach(ext => {
  220. expect(utils.isPreviewSupported(ext)).toEqual(true)
  221. })
  222. expect(utils.isPreviewSupported('foo')).toEqual(false)
  223. })
  224. })
  225. describe('isObjectURL', () => {
  226. it('should return true if the specified url is an object url', () => {
  227. expect(utils.isObjectURL('blob:abc123')).toEqual(true)
  228. expect(utils.isObjectURL('kblob:abc123')).toEqual(false)
  229. expect(utils.isObjectURL('blob-abc123')).toEqual(false)
  230. expect(utils.isObjectURL('abc123')).toEqual(false)
  231. })
  232. })
  233. describe('createThumbnail', () => {
  234. const RealCreateObjectUrl = global.URL.createObjectURL
  235. beforeEach(() => {
  236. global.URL.createObjectURL = jest.fn().mockReturnValue('newUrl')
  237. })
  238. afterEach(() => {
  239. global.URL.createObjectURL = RealCreateObjectUrl
  240. })
  241. xit(
  242. 'should create a thumbnail of the specified image at the specified width',
  243. () => {}
  244. )
  245. })
  246. describe('dataURItoBlob', () => {
  247. it('should convert a data uri to a blob', () => {
  248. const blob = utils.dataURItoBlob(sampleImageDataURI, {})
  249. expect(blob instanceof Blob).toEqual(true)
  250. expect(blob.size).toEqual(9348)
  251. expect(blob.type).toEqual('image/jpeg')
  252. })
  253. })
  254. describe('dataURItoFile', () => {
  255. it('should convert a data uri to a file', () => {
  256. const file = utils.dataURItoFile(sampleImageDataURI, { name: 'foo.jpg' })
  257. expect(file instanceof File).toEqual(true)
  258. expect(file.size).toEqual(9348)
  259. expect(file.type).toEqual('image/jpeg')
  260. expect(file.name).toEqual('foo.jpg')
  261. })
  262. })
  263. describe('getSpeed', () => {
  264. it('should calculate the speed given a fileProgress object', () => {
  265. const dateNow = new Date()
  266. const date5SecondsAgo = new Date(dateNow.getTime() - 5 * 1000)
  267. const fileProgress = {
  268. bytesUploaded: 1024,
  269. uploadStarted: date5SecondsAgo
  270. }
  271. expect(Math.round(utils.getSpeed(fileProgress))).toEqual(Math.round(205))
  272. })
  273. })
  274. describe('getBytesRemaining', () => {
  275. it('should calculate the bytes remaining given a fileProgress object', () => {
  276. const fileProgress = {
  277. bytesUploaded: 1024,
  278. bytesTotal: 3096
  279. }
  280. expect(utils.getBytesRemaining(fileProgress)).toEqual(2072)
  281. })
  282. })
  283. describe('getETA', () => {
  284. it('should get the ETA remaining based on a fileProgress object', () => {
  285. const dateNow = new Date()
  286. const date5SecondsAgo = new Date(dateNow.getTime() - 5 * 1000)
  287. const fileProgress = {
  288. bytesUploaded: 1024,
  289. bytesTotal: 3096,
  290. uploadStarted: date5SecondsAgo
  291. }
  292. expect(utils.getETA(fileProgress)).toEqual(10.1)
  293. })
  294. })
  295. describe('prettyETA', () => {
  296. it('should convert the specified number of seconds to a pretty ETA', () => {
  297. expect(utils.prettyETA(0)).toEqual('0s')
  298. expect(utils.prettyETA(1.2)).toEqual('1s')
  299. expect(utils.prettyETA(1)).toEqual('1s')
  300. expect(utils.prettyETA(103)).toEqual('1m 43s')
  301. expect(utils.prettyETA(1034.9)).toEqual('17m 14s')
  302. expect(utils.prettyETA(103984.1)).toEqual('4h 53m 04s')
  303. })
  304. })
  305. describe('copyToClipboard', () => {
  306. xit('should copy the specified text to the clipboard', () => {})
  307. })
  308. describe('getSocketHost', () => {
  309. it('should get the host from the specified url', () => {
  310. expect(
  311. utils.getSocketHost('https://foo.bar/a/b/cd?e=fghi&l=k&m=n')
  312. ).toEqual('ws://foo.bar/a/b/cd?e=fghi&l=k&m=n')
  313. })
  314. })
  315. describe('settle', () => {
  316. it('should resolve even if all input promises reject', () => {
  317. return expect(
  318. utils.settle([
  319. Promise.reject(new Error('oops')),
  320. Promise.reject(new Error('this went wrong'))
  321. ])
  322. ).resolves.toMatchObject({
  323. successful: [],
  324. failed: [{ message: 'oops' }, { message: 'this went wrong' }]
  325. })
  326. })
  327. it('should resolve with an object if some input promises resolve', () => {
  328. return expect(
  329. utils.settle([
  330. Promise.reject(new Error('rejected')),
  331. Promise.resolve('resolved'),
  332. Promise.resolve('also-resolved')
  333. ])
  334. ).resolves.toMatchObject({
  335. successful: ['resolved', 'also-resolved'],
  336. failed: [{ message: 'rejected' }]
  337. })
  338. })
  339. })
  340. describe('limitPromises', () => {
  341. let pending = 0
  342. function fn () {
  343. pending++
  344. return new Promise((resolve) => setTimeout(resolve, 10))
  345. .then(() => pending--)
  346. }
  347. it('should run at most N promises at the same time', () => {
  348. const limit = utils.limitPromises(4)
  349. const fn2 = limit(fn)
  350. const result = Promise.all([
  351. fn2(), fn2(), fn2(), fn2(),
  352. fn2(), fn2(), fn2(), fn2(),
  353. fn2(), fn2()
  354. ])
  355. expect(pending).toBe(4)
  356. setTimeout(() => {
  357. expect(pending).toBe(4)
  358. }, 10)
  359. return result.then(() => {
  360. expect(pending).toBe(0)
  361. })
  362. })
  363. it('should accept Infinity as limit', () => {
  364. const limit = utils.limitPromises(Infinity)
  365. const fn2 = limit(fn)
  366. const result = Promise.all([
  367. fn2(), fn2(), fn2(), fn2(),
  368. fn2(), fn2(), fn2(), fn2(),
  369. fn2(), fn2()
  370. ])
  371. expect(pending).toBe(10)
  372. return result.then(() => {
  373. expect(pending).toBe(0)
  374. })
  375. })
  376. })
  377. })