utils.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. /* eslint-disable compat/compat */
  2. /* global window, capabilities */
  3. const path = require('path')
  4. const { spawn } = require('child_process')
  5. const { promisify } = require('util')
  6. // This function must be valid ES5, because it is run in the browser
  7. // and IE10/IE11 do not support new syntax features
  8. function selectFakeFile (uppyID, name, type, b64) {
  9. if (!b64) b64 = 'PHN2ZyB2aWV3Qm94PSIwIDAgMTIwIDEyMCI+CiAgPGNpcmNsZSBjeD0iNjAiIGN5PSI2MCIgcj0iNTAiLz4KPC9zdmc+Cg=='
  10. if (!type) type = 'image/svg+xml'
  11. // https://stackoverflow.com/questions/16245767/creating-a-blob-from-a-base64-string-in-javascript
  12. function base64toBlob (base64Data, contentType) {
  13. contentType = contentType || ''
  14. var sliceSize = 1024
  15. var byteCharacters = atob(base64Data)
  16. var bytesLength = byteCharacters.length
  17. var slicesCount = Math.ceil(bytesLength / sliceSize)
  18. var byteArrays = new Array(slicesCount)
  19. for (var sliceIndex = 0; sliceIndex < slicesCount; ++sliceIndex) {
  20. var begin = sliceIndex * sliceSize
  21. var end = Math.min(begin + sliceSize, bytesLength)
  22. var bytes = new Array(end - begin)
  23. for (var offset = begin, i = 0; offset < end; ++i, ++offset) {
  24. bytes[i] = byteCharacters[offset].charCodeAt(0)
  25. }
  26. byteArrays[sliceIndex] = new Uint8Array(bytes)
  27. }
  28. return new Blob(byteArrays, { type: contentType })
  29. }
  30. var blob = base64toBlob(b64, type)
  31. window[uppyID].addFile({
  32. source: 'test',
  33. name: name || 'test-file',
  34. type: blob.type,
  35. data: blob,
  36. })
  37. }
  38. function ensureInputVisible (selector) {
  39. var input = document.querySelector(selector)
  40. input.style = 'width: auto; height: auto; opacity: 1; z-index: 199'
  41. input.removeAttribute('hidden')
  42. input.removeAttribute('aria-hidden')
  43. input.removeAttribute('tabindex')
  44. }
  45. function supportsChooseFile () {
  46. // no remote file uploads right now...
  47. if (process.env.CI) return false
  48. // Webdriver for Safari and Edge doesn’t support .chooseFile
  49. return capabilities.browserName !== 'Safari'
  50. && capabilities.browserName !== 'MicrosoftEdge'
  51. && capabilities.platformName !== 'Android'
  52. }
  53. function prematureExit () {
  54. throw new Error('Companion exited early')
  55. }
  56. class CompanionService {
  57. onPrepare () {
  58. this.companion = spawn('node', [
  59. path.join(__dirname, '../../packages/@uppy/companion/lib/standalone/start-server'),
  60. ], {
  61. stdio: 'pipe',
  62. env: {
  63. ...process.env,
  64. COMPANION_DATADIR: path.join(__dirname, '../../output'),
  65. COMPANION_DOMAIN: 'localhost:3030',
  66. COMPANION_PROTOCOL: 'http',
  67. COMPANION_PORT: 3030,
  68. COMPANION_SECRET: process.env.TEST_COMPANION_SECRET,
  69. COMPANION_DROPBOX_KEY: process.env.TEST_COMPANION_DROPBOX_KEY,
  70. COMPANION_DROPBOX_SECRET: process.env.TEST_COMPANION_DROPBOX_SECRET,
  71. COMPANION_GOOGLE_KEY: process.env.TEST_COMPANION_GOOGLE_KEY,
  72. COMPANION_GOOGLE_SECRET: process.env.TEST_COMPANION_GOOGLE_SECRET,
  73. },
  74. })
  75. return new Promise((resolve, reject) => {
  76. this.companion.on('error', reject)
  77. this.companion.stdout.on('data', (chunk) => {
  78. if (`${chunk}`.includes('Listening on')) {
  79. resolve()
  80. }
  81. })
  82. this.companion.on('error', console.error)
  83. this.companion.stderr.pipe(process.stderr)
  84. this.companion.on('exit', prematureExit)
  85. })
  86. }
  87. onComplete () {
  88. return new Promise((resolve) => {
  89. this.companion.removeListener('exit', prematureExit)
  90. this.companion.on('exit', () => resolve())
  91. this.companion.kill('SIGINT')
  92. })
  93. }
  94. }
  95. const express = require('express')
  96. class StaticServerService {
  97. constructor ({ folders, staticServerPort = 4567 }) {
  98. this.folders = folders
  99. this.port = staticServerPort
  100. }
  101. async onPrepare () {
  102. if (!this.folders) return
  103. this.app = express()
  104. for (const desc of this.folders) {
  105. this.app.use(desc.mount, express.static(desc.path))
  106. }
  107. const listen = promisify(this.app.listen.bind(this.app))
  108. this.server = await listen(this.port)
  109. }
  110. async onComplete () {
  111. if (this.server) {
  112. const close = promisify(this.server.close.bind(this.server))
  113. await close()
  114. }
  115. this.app = null
  116. }
  117. }
  118. const tus = require('tus-node-server')
  119. const os = require('os')
  120. const rimraf = promisify(require('rimraf'))
  121. const { randomBytes } = require('crypto')
  122. const http = require('http')
  123. const httpProxy = require('http-proxy')
  124. const brake = require('brake')
  125. class TusService {
  126. constructor ({ tusServerPort = 1080 }) {
  127. this.port = tusServerPort
  128. this.path = path.join(os.tmpdir(), `uppy-e2e-tus-node-server-${randomBytes(6).toString('hex')}`)
  129. }
  130. async onPrepare () {
  131. this.tusServer = new tus.Server()
  132. this.tusServer.datastore = new tus.FileStore({
  133. path: '/files',
  134. directory: this.path,
  135. })
  136. const proxy = httpProxy.createProxyServer()
  137. this.slowServer = http.createServer((req, res) => {
  138. proxy.web(req, res, {
  139. target: 'http://localhost:1080',
  140. // 200 kbps max upload, checking the rate limit every 20ms
  141. buffer: req.pipe(brake({
  142. period: 20,
  143. rate: 200 * 1024 / 50,
  144. })),
  145. }, (err) => { // eslint-disable-line handle-callback-err
  146. // ignore, typically a cancelled request
  147. })
  148. })
  149. const listen = promisify(this.tusServer.listen.bind(this.tusServer))
  150. this.server = await listen({ host: '0.0.0.0', port: this.port })
  151. const listen2 = promisify(this.slowServer.listen.bind(this.slowServer))
  152. await listen2(this.port + 1)
  153. }
  154. async onComplete () {
  155. if (this.slowServer) {
  156. const close = promisify(this.slowServer.close.bind(this.slowServer))
  157. await close()
  158. }
  159. if (this.server) {
  160. const close = promisify(this.server.close.bind(this.server))
  161. await close()
  162. }
  163. await rimraf(this.path)
  164. this.slowServer = null
  165. this.tusServer = null
  166. }
  167. }
  168. module.exports = {
  169. selectFakeFile,
  170. ensureInputVisible,
  171. supportsChooseFile,
  172. CompanionService,
  173. StaticServerService,
  174. TusService,
  175. }