Utils.test.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  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('every', () => {
  40. it('should return true if every array element passes predicate', () => {
  41. const arr = ['foo', 'bar', 'boo', 'moo']
  42. const predicateFn = jest.fn().mockReturnValue(true)
  43. expect(utils.every(arr, predicateFn)).toEqual(true)
  44. })
  45. it('should return false if one or more array elements fails predicate', () => {
  46. const arr = ['foo', 'bar', 'boo', 'moo']
  47. const predicateFn = jest
  48. .fn()
  49. .mockReturnValue(true)
  50. .mockReturnValueOnce(false)
  51. .mockReturnValueOnce(true)
  52. .mockReturnValueOnce(true)
  53. expect(utils.every(arr, predicateFn)).toEqual(false)
  54. })
  55. })
  56. describe('flatten', () => {
  57. it('should flatten nested arrays', () => {
  58. const nestedArrays = [['a', 'b', 'c'], ['d', 'e']]
  59. expect(utils.flatten(nestedArrays)).toEqual(['a', 'b', 'c', 'd', 'e'])
  60. })
  61. })
  62. describe('groupBy', () => {
  63. it('should partiton an array by a grouping function, returning a map', () => {
  64. const arr = [0, 10, 3, 20, 2, 4, 9]
  65. const groupingFn = val => {
  66. return val < 5
  67. }
  68. expect(utils.groupBy(arr, groupingFn)).toEqual(
  69. new Map([[true, [0, 3, 2, 4]], [false, [10, 20, 9]]])
  70. )
  71. })
  72. })
  73. describe('extend', () => {
  74. it('should merge the passed in objects', () => {
  75. const obj1 = { foo: 'bar', boo: 'moo' }
  76. const obj2 = { root: 'toot' }
  77. expect(utils.extend(obj1, obj2)).toEqual({
  78. foo: 'bar',
  79. boo: 'moo',
  80. root: 'toot'
  81. })
  82. })
  83. })
  84. describe('runPromiseSequence', () => {
  85. it('should run an array of promise-returning functions in sequence', () => {
  86. const promiseFn1 = jest.fn().mockReturnValue(Promise.resolve)
  87. const promiseFn2 = jest.fn().mockReturnValue(Promise.resolve)
  88. const promiseFn3 = jest.fn().mockReturnValue(Promise.resolve)
  89. return utils
  90. .runPromiseSequence([promiseFn1, promiseFn2, promiseFn3])
  91. .then(() => {
  92. expect(promiseFn1.mock.calls.length).toEqual(1)
  93. expect(promiseFn2.mock.calls.length).toEqual(1)
  94. expect(promiseFn3.mock.calls.length).toEqual(1)
  95. })
  96. })
  97. })
  98. describe('supportsMediaRecorder', () => {
  99. it('should return true if MediaRecorder is supported', () => {
  100. global.MediaRecorder = () => {}
  101. global.MediaRecorder.prototype.start = () => {}
  102. expect(utils.supportsMediaRecorder()).toEqual(true)
  103. })
  104. it('should return false if MediaRecorder is not supported', () => {
  105. global.MediaRecorder = undefined
  106. expect(utils.supportsMediaRecorder()).toEqual(false)
  107. global.MediaRecorder = () => {}
  108. expect(utils.supportsMediaRecorder()).toEqual(false)
  109. global.MediaRecorder.prototype.foo = () => {}
  110. expect(utils.supportsMediaRecorder()).toEqual(false)
  111. })
  112. })
  113. describe('isTouchDevice', () => {
  114. it("should return true if it's a touch device", () => {
  115. global.window.ontouchstart = () => {}
  116. expect(utils.isTouchDevice()).toEqual(true)
  117. delete global.window.ontouchstart
  118. global.navigator.maxTouchPoints = () => {}
  119. expect(utils.isTouchDevice()).toEqual(true)
  120. })
  121. })
  122. describe('getFileNameAndExtension', () => {
  123. it('should return the filename and extension as an array', () => {
  124. expect(utils.getFileNameAndExtension('fsdfjodsuf23rfw.jpg')).toEqual([
  125. 'fsdfjodsuf23rfw',
  126. 'jpg'
  127. ])
  128. })
  129. it('should handle invalid filenames', () => {
  130. expect(utils.getFileNameAndExtension('fsdfjodsuf23rfw')).toEqual([
  131. 'fsdfjodsuf23rfw',
  132. undefined
  133. ])
  134. })
  135. })
  136. describe('truncateString', () => {
  137. it('should truncate the string by the specified amount', () => {
  138. expect(utils.truncateString('abcdefghijkl', 10)).toEqual('abcde...jkl')
  139. expect(utils.truncateString('abcdefghijkl', 9)).toEqual('abcd...jkl')
  140. expect(utils.truncateString('abcdefghijkl', 8)).toEqual('abcd...kl')
  141. expect(utils.truncateString('abcdefghijkl', 7)).toEqual('abc...kl')
  142. expect(utils.truncateString('abcdefghijkl', 6)).toEqual('abc...kl')
  143. expect(utils.truncateString('abcdefghijkl', 5)).toEqual('ab...kl')
  144. expect(utils.truncateString('abcdefghijkl', 4)).toEqual('ab...l')
  145. expect(utils.truncateString('abcdefghijkl', 3)).toEqual('a...l')
  146. expect(utils.truncateString('abcdefghijkl', 2)).toEqual('a...l')
  147. expect(utils.truncateString('abcdefghijkl', 1)).toEqual('...l')
  148. })
  149. })
  150. describe('secondsToTime', () => {
  151. expect(utils.secondsToTime(60)).toEqual({
  152. hours: 0,
  153. minutes: 1,
  154. seconds: 0
  155. })
  156. expect(utils.secondsToTime(123)).toEqual({
  157. hours: 0,
  158. minutes: 2,
  159. seconds: 3
  160. })
  161. expect(utils.secondsToTime(1060)).toEqual({
  162. hours: 0,
  163. minutes: 17,
  164. seconds: 40
  165. })
  166. expect(utils.secondsToTime(123453460)).toEqual({
  167. hours: 20,
  168. minutes: 37,
  169. seconds: 40
  170. })
  171. })
  172. describe('getFileTypeExtension', () => {
  173. it('should return the filetype based on the specified mime type', () => {
  174. expect(utils.getFileTypeExtension('video/ogg')).toEqual('ogv')
  175. expect(utils.getFileTypeExtension('audio/ogg')).toEqual('ogg')
  176. expect(utils.getFileTypeExtension('video/webm')).toEqual('webm')
  177. expect(utils.getFileTypeExtension('audio/webm')).toEqual('webm')
  178. expect(utils.getFileTypeExtension('video/mp4')).toEqual('mp4')
  179. expect(utils.getFileTypeExtension('audio/mp3')).toEqual('mp3')
  180. expect(utils.getFileTypeExtension('foo/bar')).toEqual(null)
  181. })
  182. })
  183. describe('getFileType', () => {
  184. beforeEach(() => {
  185. global.FileReader = class FileReader {
  186. addEventListener (e, cb) {
  187. if (e === 'load') {
  188. this.loadCb = cb
  189. }
  190. if (e === 'error') {
  191. this.errorCb = cb
  192. }
  193. }
  194. readAsArrayBuffer (chunk) {
  195. this.loadCb({ target: { result: new ArrayBuffer(8) } })
  196. }
  197. }
  198. })
  199. afterEach(() => {
  200. global.FileReader = undefined
  201. })
  202. it('should trust the filetype if the file comes from a remote source', () => {
  203. const file = {
  204. isRemote: true,
  205. type: 'audio/webm',
  206. name: 'foo.webm'
  207. }
  208. return utils.getFileType(file).then(r => {
  209. expect(r).toEqual(['audio', 'webm'])
  210. })
  211. })
  212. it('should determine the filetype from the mimetype', () => {
  213. const file = {
  214. type: 'audio/webm',
  215. name: 'foo.webm',
  216. data: 'sdfsdfhq9efbicw'
  217. }
  218. return utils.getFileType(file).then(r => {
  219. expect(r).toEqual(['audio', 'webm'])
  220. })
  221. })
  222. it('should determine the filetype from the extension', () => {
  223. const file = {
  224. name: 'foo.mp3',
  225. data: 'sdfsfhfh329fhwihs'
  226. }
  227. return utils.getFileType(file).then(r => {
  228. expect(r).toEqual(['audio', 'mp3'])
  229. })
  230. })
  231. it('should fail gracefully if unable to detect', () => {
  232. const file = {
  233. name: 'foobar',
  234. data: 'sdfsfhfh329fhwihs'
  235. }
  236. return utils.getFileType(file).then(r => {
  237. expect(r).toEqual(['', ''])
  238. })
  239. })
  240. })
  241. describe('getArrayBuffer', () => {
  242. beforeEach(() => {
  243. global.FileReader = class FileReader {
  244. addEventListener (e, cb) {
  245. if (e === 'load') {
  246. this.loadCb = cb
  247. }
  248. if (e === 'error') {
  249. this.errorCb = cb
  250. }
  251. }
  252. readAsArrayBuffer (chunk) {
  253. this.loadCb({ target: { result: new ArrayBuffer(8) } })
  254. }
  255. }
  256. })
  257. afterEach(() => {
  258. global.FileReader = undefined
  259. })
  260. it('should return a promise that resolves with the specified chunk', () => {
  261. return utils.getArrayBuffer('abcde').then(buffer => {
  262. expect(typeof buffer).toEqual('object')
  263. expect(buffer.byteLength).toEqual(8)
  264. })
  265. })
  266. })
  267. describe('isPreviewSupported', () => {
  268. it('should return true for any filetypes that browsers can preview', () => {
  269. const supported = ['jpeg', 'gif', 'png', 'svg', 'svg+xml', 'bmp']
  270. supported.forEach(ext => {
  271. expect(utils.isPreviewSupported(ext)).toEqual(true)
  272. })
  273. expect(utils.isPreviewSupported('foo')).toEqual(false)
  274. })
  275. })
  276. describe('isObjectURL', () => {
  277. it('should return true if the specified url is an object url', () => {
  278. expect(utils.isObjectURL('blob:abc123')).toEqual(true)
  279. expect(utils.isObjectURL('kblob:abc123')).toEqual(false)
  280. expect(utils.isObjectURL('blob-abc123')).toEqual(false)
  281. expect(utils.isObjectURL('abc123')).toEqual(false)
  282. })
  283. })
  284. describe('createThumbnail', () => {
  285. xit(
  286. 'should create a thumbnail of the specified image at the specified width',
  287. () => {}
  288. )
  289. })
  290. describe('dataURItoBlob', () => {
  291. it('should convert a data uri to a blob', () => {
  292. const blob = utils.dataURItoBlob(sampleImageDataURI, {})
  293. expect(blob instanceof Blob).toEqual(true)
  294. expect(blob.size).toEqual(9348)
  295. expect(blob.type).toEqual('image/jpeg')
  296. })
  297. })
  298. describe('dataURItoFile', () => {
  299. it('should convert a data uri to a file', () => {
  300. const file = utils.dataURItoFile(sampleImageDataURI, { name: 'foo.jpg' })
  301. expect(file instanceof File).toEqual(true)
  302. expect(file.size).toEqual(9348)
  303. expect(file.type).toEqual('image/jpeg')
  304. expect(file.name).toEqual('foo.jpg')
  305. })
  306. })
  307. describe('getSpeed', () => {
  308. it('should calculate the speed given a fileProgress object', () => {
  309. const dateNow = new Date()
  310. const date5SecondsAgo = new Date(dateNow.getTime() - 5 * 1000)
  311. const fileProgress = {
  312. bytesUploaded: 1024,
  313. uploadStarted: date5SecondsAgo
  314. }
  315. expect(Math.round(utils.getSpeed(fileProgress))).toEqual(Math.round(205))
  316. })
  317. })
  318. describe('getBytesRemaining', () => {
  319. it('should calculate the bytes remaining given a fileProgress object', () => {
  320. const fileProgress = {
  321. bytesUploaded: 1024,
  322. bytesTotal: 3096
  323. }
  324. expect(utils.getBytesRemaining(fileProgress)).toEqual(2072)
  325. })
  326. })
  327. describe('getETA', () => {
  328. it('should get the ETA remaining based on a fileProgress object', () => {
  329. const dateNow = new Date()
  330. const date5SecondsAgo = new Date(dateNow.getTime() - 5 * 1000)
  331. const fileProgress = {
  332. bytesUploaded: 1024,
  333. bytesTotal: 3096,
  334. uploadStarted: date5SecondsAgo
  335. }
  336. expect(utils.getETA(fileProgress)).toEqual(10.1)
  337. })
  338. })
  339. describe('prettyETA', () => {
  340. it('should convert the specified number of seconds to a pretty ETA', () => {
  341. expect(utils.prettyETA(0)).toEqual('0s')
  342. expect(utils.prettyETA(1.2)).toEqual('1s')
  343. expect(utils.prettyETA(1)).toEqual('1s')
  344. expect(utils.prettyETA(103)).toEqual('1m 43s')
  345. expect(utils.prettyETA(1034.9)).toEqual('17m 14s')
  346. expect(utils.prettyETA(103984.1)).toEqual('4h 53m 04s')
  347. })
  348. })
  349. describe('copyToClipboard', () => {
  350. xit('should copy the specified text to the clipboard', () => {})
  351. })
  352. describe('getSocketHost', () => {
  353. it('should get the host from the specified url', () => {
  354. expect(
  355. utils.getSocketHost('https://foo.bar/a/b/cd?e=fghi&l=k&m=n')
  356. ).toEqual('ws://foo.bar/a/b/cd?e=fghi&l=k&m=n')
  357. })
  358. })
  359. })