utils.js 6.1 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_BOX_KEY: process.env.TEST_COMPANION_BOX_KEY,
  72. COMPANION_BOX_SECRET: process.env.TEST_COMPANION_BOX_SECRET,
  73. COMPANION_GOOGLE_KEY: process.env.TEST_COMPANION_GOOGLE_KEY,
  74. COMPANION_GOOGLE_SECRET: process.env.TEST_COMPANION_GOOGLE_SECRET
  75. }
  76. })
  77. return new Promise((resolve, reject) => {
  78. this.companion.on('error', reject)
  79. this.companion.stdout.on('data', (chunk) => {
  80. if (`${chunk}`.includes('Listening on')) {
  81. resolve()
  82. }
  83. })
  84. this.companion.on('error', console.error)
  85. this.companion.stderr.pipe(process.stderr)
  86. this.companion.on('exit', prematureExit)
  87. })
  88. }
  89. onComplete () {
  90. return new Promise((resolve) => {
  91. this.companion.removeListener('exit', prematureExit)
  92. this.companion.on('exit', () => resolve())
  93. this.companion.kill('SIGINT')
  94. })
  95. }
  96. }
  97. const express = require('express')
  98. class StaticServerService {
  99. constructor ({ folders, staticServerPort = 4567 }) {
  100. this.folders = folders
  101. this.port = staticServerPort
  102. }
  103. async onPrepare () {
  104. if (!this.folders) return
  105. this.app = express()
  106. for (const desc of this.folders) {
  107. this.app.use(desc.mount, express.static(desc.path))
  108. }
  109. const listen = promisify(this.app.listen.bind(this.app))
  110. this.server = await listen(this.port)
  111. }
  112. async onComplete () {
  113. if (this.server) {
  114. const close = promisify(this.server.close.bind(this.server))
  115. await close()
  116. }
  117. this.app = null
  118. }
  119. }
  120. const tus = require('tus-node-server')
  121. const os = require('os')
  122. const rimraf = promisify(require('rimraf'))
  123. const { randomBytes } = require('crypto')
  124. const http = require('http')
  125. const httpProxy = require('http-proxy')
  126. const brake = require('brake')
  127. class TusService {
  128. constructor ({ tusServerPort = 1080 }) {
  129. this.port = tusServerPort
  130. this.path = path.join(os.tmpdir(), `uppy-e2e-tus-node-server-${randomBytes(6).toString('hex')}`)
  131. }
  132. async onPrepare () {
  133. this.tusServer = new tus.Server()
  134. this.tusServer.datastore = new tus.FileStore({
  135. path: '/files',
  136. directory: this.path
  137. })
  138. const proxy = httpProxy.createProxyServer()
  139. this.slowServer = http.createServer((req, res) => {
  140. proxy.web(req, res, {
  141. target: 'http://localhost:1080',
  142. // 200 kbps max upload, checking the rate limit every 20ms
  143. buffer: req.pipe(brake({
  144. period: 20,
  145. rate: 200 * 1024 / 50
  146. }))
  147. }, (err) => { // eslint-disable-line handle-callback-err
  148. // ignore, typically a cancelled request
  149. })
  150. })
  151. const listen = promisify(this.tusServer.listen.bind(this.tusServer))
  152. this.server = await listen({ host: '0.0.0.0', port: this.port })
  153. const listen2 = promisify(this.slowServer.listen.bind(this.slowServer))
  154. await listen2(this.port + 1)
  155. }
  156. async onComplete () {
  157. if (this.slowServer) {
  158. const close = promisify(this.slowServer.close.bind(this.slowServer))
  159. await close()
  160. }
  161. if (this.server) {
  162. const close = promisify(this.server.close.bind(this.server))
  163. await close()
  164. }
  165. await rimraf(this.path)
  166. this.slowServer = null
  167. this.tusServer = null
  168. }
  169. }
  170. module.exports = {
  171. selectFakeFile,
  172. ensureInputVisible,
  173. supportsChooseFile,
  174. CompanionService,
  175. StaticServerService,
  176. TusService
  177. }