diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f99ad1d..b02ff57 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,7 +7,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest] - node: ['8', '10', '12'] + node: ['8', '10', '12', '13'] steps: - uses: actions/checkout@v1 - name: Setup Node.js v${{ matrix.node }} diff --git a/changelog.md b/changelog.md index 32e3840..d1b6703 100644 --- a/changelog.md +++ b/changelog.md @@ -6,6 +6,10 @@ - Updated Node.js support from v8.5+ to v8.10+, to match what the [`eslint`](https://npm.im/eslint) dev dependency now supports. This is unlikely to be a breaking change for the published package. +### Minor + +- Updated the [`fs-capacitor`](https://npm.im/fs-capacitor) dependency to v4 to support Node.js v13, making required changes to the source and tests, via [#166](https://github.com/jaydenseric/graphql-upload/pull/166). + ### Patch - Updated dev dependencies. @@ -13,6 +17,7 @@ - [`tap`](https://npm.im/tap) now ignores test files by default. - The `lib/test-helpers` directory is now ignored using [`tap`](https://npm.im/tap) CLI arguments due to [tapjs/node-tap#612](https://github.com/tapjs/node-tap/issues/612). - Updated the “Support” section of the readme to clarify varying native ESM support across Node.js versions. +- No longer test [`fs-capacitor`](https://npm.im/fs-capacitor) implementation details such as temp file creation and cleanup. ## 8.1.0 diff --git a/package.json b/package.json index 6e504d0..22a0b7c 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ }, "dependencies": { "busboy": "^0.3.1", - "fs-capacitor": "^2.0.4", + "fs-capacitor": "^4.0.0", "http-errors": "^1.7.3", "object-path": "^0.11.4" }, diff --git a/src/processRequest.mjs b/src/processRequest.mjs index f569d42..b4d1c49 100644 --- a/src/processRequest.mjs +++ b/src/processRequest.mjs @@ -1,12 +1,14 @@ import util from 'util' import Busboy from 'busboy' -import { WriteStream } from 'fs-capacitor' +import fsCapacitor from 'fs-capacitor' import createError from 'http-errors' import objectPath from 'object-path' import { SPEC_URL } from './constants' import { ignoreStream } from './ignoreStream' import { isEnumerableObject } from './isEnumerableObject' +const { WriteStream } = fsCapacitor + /** * An expected file upload. * @kind class @@ -139,7 +141,7 @@ export const processRequest = ( if (map) for (const upload of map.values()) - if (upload.file) upload.file.capacitor.destroy() + if (upload.file) upload.file.capacitor.release() } /** @@ -302,6 +304,7 @@ export const processRequest = ( return } + let fileError const capacitor = new WriteStream() capacitor.on('error', () => { @@ -310,29 +313,27 @@ export const processRequest = ( }) stream.on('limit', () => { - stream.unpipe() - capacitor.destroy( - createError( - 413, - `File truncated as it exceeds the ${maxFileSize} byte size limit.` - ) + fileError = createError( + 413, + `File truncated as it exceeds the ${maxFileSize} byte size limit.` ) + stream.unpipe() + capacitor.destroy(fileError) }) stream.on('error', error => { + fileError = error stream.unpipe() // istanbul ignore next capacitor.destroy(exitError || error) }) - stream.pipe(capacitor) - const file = { filename, mimetype, encoding, createReadStream() { - const error = capacitor.error || (released ? exitError : null) + const error = fileError || (released ? exitError : null) if (error) throw error return capacitor.createReadStream() } @@ -348,6 +349,7 @@ export const processRequest = ( Object.defineProperty(file, 'capacitor', { value: capacitor }) + stream.pipe(capacitor) upload.resolve(file) }) diff --git a/src/processRequest.test.mjs b/src/processRequest.test.mjs index 5c762b5..f90220f 100644 --- a/src/processRequest.test.mjs +++ b/src/processRequest.test.mjs @@ -1,10 +1,9 @@ -import fs from 'fs' import http from 'http' import stream from 'stream' import express from 'express' import expressAsyncHandler from 'express-async-handler' import FormData from 'form-data' -import { ReadStream } from 'fs-capacitor' +import fsCapacitor from 'fs-capacitor' import Koa from 'koa' import fetch from 'node-fetch' import t from 'tap' @@ -15,6 +14,8 @@ import { snapshotError } from './test-helpers/snapshotError' import { startServer } from './test-helpers/startServer' import { streamToString } from './test-helpers/streamToString' +const { ReadStream } = fsCapacitor + t.test('Single file.', async t => { const sendRequest = async port => { const body = new FormData() @@ -36,9 +37,7 @@ t.test('Single file.', async t => { } await t.test('Node.js HTTP.', async t => { - t.plan(2) - - let variables + t.plan(1) const app = http.createServer(async (request, response) => { const send = data => { @@ -57,7 +56,6 @@ t.test('Single file.', async t => { try { const body = await processRequest(request, response) - ;({ variables } = body) await t.test('Upload.', uploadTest(body.variables.file)) send() } catch (error) { @@ -70,20 +68,12 @@ t.test('Single file.', async t => { const port = await startServer(t, app) await sendRequest(port) - - const file = await variables.file - if (!file.capacitor.closed) - await new Promise(resolve => file.capacitor.once('close', resolve)) - t.false(fs.existsSync(file.capacitor.path), 'Cleanup.') }) await t.test('Koa middleware.', async t => { - t.plan(2) - - let variables + t.plan(1) const app = new Koa().use(graphqlUploadKoa()).use(async (ctx, next) => { - ;({ variables } = ctx.request.body) await t.test('Upload.', uploadTest(ctx.request.body.variables.file)) ctx.status = 204 @@ -93,23 +83,15 @@ t.test('Single file.', async t => { const port = await startServer(t, app) await sendRequest(port) - - const file = await variables.file - if (!file.capacitor.closed) - await new Promise(resolve => file.capacitor.once('close', resolve)) - t.false(fs.existsSync(file.capacitor.path), 'Cleanup.') }) await t.test('Express middleware.', async t => { - t.plan(2) - - let variables + t.plan(1) const app = express() .use(graphqlUploadExpress()) .use( expressAsyncHandler(async (request, response, next) => { - ;({ variables } = request.body) await t.test('Upload.', uploadTest(request.body.variables.file)) next() }) @@ -118,11 +100,6 @@ t.test('Single file.', async t => { const port = await startServer(t, app) await sendRequest(port) - - const file = await variables.file - if (!file.capacitor.closed) - await new Promise(resolve => file.capacitor.once('close', resolve)) - t.false(fs.existsSync(file.capacitor.path), 'Cleanup.') }) }) @@ -163,13 +140,9 @@ t.test('Single file batched.', async t => { } await t.test('Koa middleware.', async t => { - t.plan(4) - - let operations + t.plan(2) const app = new Koa().use(graphqlUploadKoa()).use(async (ctx, next) => { - operations = ctx.request.body - await Promise.all([ t.test('Upload A.', uploadATest(ctx.request.body[0].variables.file)), t.test('Upload B.', uploadBTest(ctx.request.body[1].variables.file)) @@ -182,28 +155,15 @@ t.test('Single file batched.', async t => { const port = await startServer(t, app) await sendRequest(port) - - const fileA = await operations[0].variables.file - if (!fileA.capacitor.closed) - await new Promise(resolve => fileA.capacitor.once('close', resolve)) - t.false(fs.existsSync(fileA.capacitor.path), 'Cleanup A.') - - const fileB = await operations[1].variables.file - if (!fileB.capacitor.closed) - await new Promise(resolve => fileB.capacitor.once('close', resolve)) - t.false(fs.existsSync(fileB.capacitor.path), 'Cleanup B.') }) await t.test('Express middleware.', async t => { - t.plan(4) - - let operations + t.plan(2) const app = express() .use(graphqlUploadExpress()) .use( expressAsyncHandler(async (request, response, next) => { - operations = request.body await Promise.all([ t.test('Upload A.', uploadATest(request.body[0].variables.file)), t.test('Upload B.', uploadBTest(request.body[1].variables.file)) @@ -215,16 +175,6 @@ t.test('Single file batched.', async t => { const port = await startServer(t, app) await sendRequest(port) - - const fileA = await operations[0].variables.file - if (!fileA.capacitor.closed) - await new Promise(resolve => fileA.capacitor.once('close', resolve)) - t.false(fs.existsSync(fileA.capacitor.path), 'Cleanup A.') - - const fileB = await operations[1].variables.file - if (!fileB.capacitor.closed) - await new Promise(resolve => fileB.capacitor.once('close', resolve)) - t.false(fs.existsSync(fileB.capacitor.path), 'Cleanup B.') }) }) @@ -592,13 +542,9 @@ t.test('Handles unconsumed uploads.', async t => { } await t.test('Koa middleware.', async t => { - t.plan(3) - - let variables + t.plan(1) const app = new Koa().use(graphqlUploadKoa()).use(async (ctx, next) => { - ;({ variables } = ctx.request.body) - await t.test('Upload B.', uploadBTest(ctx.request.body.variables.fileB)) ctx.status = 204 @@ -608,28 +554,15 @@ t.test('Handles unconsumed uploads.', async t => { const port = await startServer(t, app) await sendRequest(port) - - const fileA = await variables.fileA - if (!fileA.capacitor.closed) - await new Promise(resolve => fileA.capacitor.once('close', resolve)) - t.false(fs.existsSync(fileA.capacitor.path), 'Cleanup A.') - - const fileB = await variables.fileB - if (!fileB.capacitor.closed) - await new Promise(resolve => fileB.capacitor.once('close', resolve)) - t.false(fs.existsSync(fileB.capacitor.path), 'Cleanup B.') }) await t.test('Express middleware.', async t => { - t.plan(3) - - let variables + t.plan(1) const app = express() .use(graphqlUploadExpress()) .use( expressAsyncHandler(async (request, response, next) => { - ;({ variables } = request.body) await t.test('Upload B.', uploadBTest(request.body.variables.fileB)) next() }) @@ -638,16 +571,6 @@ t.test('Handles unconsumed uploads.', async t => { const port = await startServer(t, app) await sendRequest(port) - - const fileA = await variables.fileA - if (!fileA.capacitor.closed) - await new Promise(resolve => fileA.capacitor.once('close', resolve)) - t.false(fs.existsSync(fileA.capacitor.path), 'Cleanup A.') - - const fileB = await variables.fileB - if (!fileB.capacitor.closed) - await new Promise(resolve => fileB.capacitor.once('close', resolve)) - t.false(fs.existsSync(fileB.capacitor.path), 'Cleanup B.') }) }) @@ -763,14 +686,13 @@ t.test('Aborted request.', async t => { } await t.test('Koa middleware.', async t => { - t.plan(6) + t.plan(4) let requestHasBeenReceived const requestHasBeenReceivedPromise = new Promise( resolve => (requestHasBeenReceived = resolve) ) - let variables let finish const finished = new Promise(resolve => (finish = resolve)) @@ -784,14 +706,29 @@ t.test('Aborted request.', async t => { }) .use(graphqlUploadKoa()) .use(async (ctx, next) => { - ;({ variables } = ctx.request.body) - const fileA = await ctx.request.body.variables.fileA const fileB = await ctx.request.body.variables.fileB const streamA = fileA.createReadStream() + + streamA.once('end', () => { + streamA.ended = true + }) + + streamA.once('error', error => { + streamA.error = error + }) + const streamB = fileB.createReadStream() + streamB.once('end', () => { + streamB.ended = true + }) + + streamB.once('error', error => { + streamB.error = error + }) + await Promise.all([ t.test('Upload A.', uploadATest(fileA, streamA)), t.test('Upload B.', uploadBTest(fileB, streamB)), @@ -807,27 +744,16 @@ t.test('Aborted request.', async t => { await sendRequest(port, requestHasBeenReceivedPromise) await finished - - const fileA = await variables.fileA - if (!fileA.capacitor.closed) - await new Promise(resolve => fileA.capacitor.once('close', resolve)) - t.false(fs.existsSync(fileA.capacitor.path), 'Cleanup A.') - - const fileB = await variables.fileB - if (!fileB.capacitor.closed) - await new Promise(resolve => fileB.capacitor.once('close', resolve)) - t.false(fs.existsSync(fileB.capacitor.path), 'Cleanup B.') }) await t.test('Express middleware.', async t => { - t.plan(5) + t.plan(3) let requestHasBeenReceived const requestHasBeenReceivedPromise = new Promise( resolve => (requestHasBeenReceived = resolve) ) - let variables let finish const finished = new Promise(resolve => (finish = resolve)) @@ -839,14 +765,29 @@ t.test('Aborted request.', async t => { .use(graphqlUploadExpress()) .use( expressAsyncHandler(async (request, response, next) => { - ;({ variables } = request.body) - const fileA = await request.body.variables.fileA const fileB = await request.body.variables.fileB const streamA = fileA.createReadStream() + + streamA.once('end', () => { + streamA.ended = true + }) + + streamA.once('error', error => { + streamA.error = error + }) + const streamB = fileB.createReadStream() + streamB.once('end', () => { + streamB.ended = true + }) + + streamB.once('error', error => { + streamB.error = error + }) + await Promise.all([ t.test('Upload A.', uploadATest(fileA, streamA)), t.test('Upload B.', uploadBTest(fileB, streamB)), @@ -862,16 +803,6 @@ t.test('Aborted request.', async t => { await sendRequest(port, requestHasBeenReceivedPromise) await finished - - const fileA = await variables.fileA - if (!fileA.capacitor.closed) - await new Promise(resolve => fileA.capacitor.once('close', resolve)) - t.false(fs.existsSync(fileA.capacitor.path), 'Cleanup A.') - - const fileB = await variables.fileB - if (!fileB.capacitor.closed) - await new Promise(resolve => fileB.capacitor.once('close', resolve)) - t.false(fs.existsSync(fileB.capacitor.path), 'Cleanup B.') }) }) @@ -897,14 +828,13 @@ t.test('Aborted request.', async t => { } await t.test('Koa middleware.', async t => { - t.plan(6) + t.plan(4) let requestHasBeenReceived const requestHasBeenReceivedPromise = new Promise( resolve => (requestHasBeenReceived = resolve) ) - let variables let finish const finished = new Promise(resolve => (finish = resolve)) @@ -918,8 +848,6 @@ t.test('Aborted request.', async t => { }) .use(graphqlUploadKoa()) .use(async (ctx, next) => { - ;({ variables } = ctx.request.body) - // This ensures that the upload has streamed in as far as it will, and // the parser has been detached. await new Promise(resolve => { @@ -946,27 +874,16 @@ t.test('Aborted request.', async t => { await sendRequest(port, requestHasBeenReceivedPromise) await finished - - const fileA = await variables.fileA - if (!fileA.capacitor.closed) - await new Promise(resolve => fileA.capacitor.once('close', resolve)) - t.false(fs.existsSync(fileA.capacitor.path), 'Cleanup A.') - - const fileB = await variables.fileB - if (!fileB.capacitor.closed) - await new Promise(resolve => fileB.capacitor.once('close', resolve)) - t.false(fs.existsSync(fileB.capacitor.path), 'Cleanup B.') }) await t.test('Express middleware.', async t => { - t.plan(5) + t.plan(3) let requestHasBeenReceived const requestHasBeenReceivedPromise = new Promise( resolve => (requestHasBeenReceived = resolve) ) - let variables let finish const finished = new Promise(resolve => (finish = resolve)) @@ -978,8 +895,6 @@ t.test('Aborted request.', async t => { .use(graphqlUploadExpress()) .use( expressAsyncHandler(async (request, response, next) => { - ;({ variables } = request.body) - // This ensures that the upload has streamed in as far as it will, and // the parser has been detached. await new Promise(resolve => { @@ -1005,16 +920,6 @@ t.test('Aborted request.', async t => { await sendRequest(port, requestHasBeenReceivedPromise) await finished - - const fileA = await variables.fileA - if (!fileA.capacitor.closed) - await new Promise(resolve => fileA.capacitor.once('close', resolve)) - t.false(fs.existsSync(fileA.capacitor.path), 'Cleanup A.') - - const fileB = await variables.fileB - if (!fileB.capacitor.closed) - await new Promise(resolve => fileB.capacitor.once('close', resolve)) - t.false(fs.existsSync(fileB.capacitor.path), 'Cleanup B.') }) }) }) @@ -1037,13 +942,9 @@ t.test('Deduped files.', async t => { } await t.test('Koa middleware.', async t => { - t.plan(7) - - let variables + t.plan(5) const app = new Koa().use(graphqlUploadKoa()).use(async (ctx, next) => { - ;({ variables } = ctx.request.body) - t.strictSame( ctx.request.body.variables.files[0], ctx.request.body.variables.files[1], @@ -1074,28 +975,15 @@ t.test('Deduped files.', async t => { const port = await startServer(t, app) await sendRequest(port) - - const fileA = await variables.files[0] - if (!fileA.capacitor.closed) - await new Promise(resolve => fileA.capacitor.once('close', resolve)) - t.false(fs.existsSync(fileA.capacitor.path), 'Cleanup A.') - - const fileB = await variables.files[1] - if (!fileB.capacitor.closed) - await new Promise(resolve => fileB.capacitor.once('close', resolve)) - t.false(fs.existsSync(fileB.capacitor.path), 'Cleanup B.') }) await t.test('Express middleware.', async t => { - t.plan(7) - - let variables + t.plan(5) const app = express() .use(graphqlUploadExpress()) .use( expressAsyncHandler(async (request, response, next) => { - ;({ variables } = request.body) t.strictSame( request.body.variables.files[0], request.body.variables.files[1], @@ -1124,16 +1012,6 @@ t.test('Deduped files.', async t => { const port = await startServer(t, app) await sendRequest(port) - - const fileA = await variables.files[0] - if (!fileA.capacitor.closed) - await new Promise(resolve => fileA.capacitor.once('close', resolve)) - t.false(fs.existsSync(fileA.capacitor.path), 'Cleanup A.') - - const fileB = await variables.files[1] - if (!fileB.capacitor.closed) - await new Promise(resolve => fileB.capacitor.once('close', resolve)) - t.false(fs.existsSync(fileB.capacitor.path), 'Cleanup B.') }) }) @@ -1212,12 +1090,9 @@ t.test('Extraneous file.', async t => { } await t.test('Koa middleware.', async t => { - t.plan(2) - - let variables + t.plan(1) const app = new Koa().use(graphqlUploadKoa()).use(async (ctx, next) => { - ;({ variables } = ctx.request.body) await t.test('Upload.', uploadTest(ctx.request.body.variables.file)) ctx.status = 204 await next() @@ -1226,23 +1101,15 @@ t.test('Extraneous file.', async t => { const port = await startServer(t, app) await sendRequest(port) - - const file = await variables.file - if (!file.capacitor.closed) - await new Promise(resolve => file.capacitor.once('close', resolve)) - t.false(fs.existsSync(file.capacitor.path), 'Cleanup.') }) await t.test('Express middleware.', async t => { - t.plan(2) - - let variables + t.plan(1) const app = express() .use(graphqlUploadExpress()) .use( expressAsyncHandler(async (request, response, next) => { - ;({ variables } = request.body) await t.test('Upload.', uploadTest(request.body.variables.file)) next() }) @@ -1251,11 +1118,6 @@ t.test('Extraneous file.', async t => { const port = await startServer(t, app) await sendRequest(port) - - const file = await variables.file - if (!file.capacitor.closed) - await new Promise(resolve => file.capacitor.once('close', resolve)) - t.false(fs.existsSync(file.capacitor.path), 'Cleanup.') }) }) @@ -1345,17 +1207,14 @@ t.test('Exceed max files with extraneous files interspersed.', async t => { } await t.test('Koa middleware.', async t => { - t.plan(3) + t.plan(2) - let variables let finish const finished = new Promise(resolve => (finish = resolve)) const app = new Koa() .use(graphqlUploadKoa({ maxFiles: 2 })) .use(async (ctx, next) => { - ;({ variables } = ctx.request.body) - await Promise.all([ t.test('Upload A.', uploadATest(ctx.request.body.variables.files[0])), t.test('Upload B.', uploadBTest(ctx.request.body.variables.files[1])) @@ -1370,24 +1229,15 @@ t.test('Exceed max files with extraneous files interspersed.', async t => { await sendRequest(port) await finished - - const fileA = await variables.files[0] - if (!fileA.capacitor.closed) - await new Promise(resolve => fileA.capacitor.once('close', resolve)) - t.false(fs.existsSync(fileA.capacitor.path), 'Cleanup A.') }) await t.test('Express middleware.', async t => { - t.plan(3) - - let variables + t.plan(2) const app = express() .use(graphqlUploadExpress({ maxFiles: 2 })) .use( expressAsyncHandler(async (request, response, next) => { - ;({ variables } = request.body) - await Promise.all([ t.test('Upload A.', uploadATest(request.body.variables.files[0])), t.test('Upload B.', uploadBTest(request.body.variables.files[1])) @@ -1400,11 +1250,6 @@ t.test('Exceed max files with extraneous files interspersed.', async t => { const port = await startServer(t, app) await sendRequest(port) - - const fileA = await variables.files[0] - if (!fileA.capacitor.closed) - await new Promise(resolve => fileA.capacitor.once('close', resolve)) - t.false(fs.existsSync(fileA.capacitor.path), 'Cleanup A.') }) }) @@ -1443,14 +1288,11 @@ t.test('Exceed max file size.', async t => { } await t.test('Koa middleware.', async t => { - t.plan(4) - - let variables + t.plan(2) const app = new Koa() .use(graphqlUploadKoa({ maxFileSize: 1 })) .use(async (ctx, next) => { - ;({ variables } = ctx.request.body) await t.test( 'Upload A.', uploadATest(ctx.request.body.variables.files[0]) @@ -1468,29 +1310,15 @@ t.test('Exceed max file size.', async t => { const port = await startServer(t, app) await sendRequest(port) - - const fileA = await variables.files[0] - if (!fileA.capacitor.closed) - await new Promise(resolve => fileA.capacitor.once('close', resolve)) - t.false(fs.existsSync(fileA.capacitor.path), 'Cleanup A.') - - const fileB = await variables.files[1] - if (!fileB.capacitor.closed) - await new Promise(resolve => fileB.capacitor.once('close', resolve)) - t.false(fs.existsSync(fileB.capacitor.path), 'Cleanup B.') }) await t.test('Express middleware.', async t => { - t.plan(4) - - let variables + t.plan(2) const app = express() .use(graphqlUploadExpress({ maxFileSize: 1 })) .use( expressAsyncHandler(async (request, response, next) => { - ;({ variables } = request.body) - await Promise.all([ t.test('Upload A.', uploadATest(request.body.variables.files[0])), t.test('Upload B.', uploadBTest(request.body.variables.files[1])) @@ -1503,16 +1331,6 @@ t.test('Exceed max file size.', async t => { const port = await startServer(t, app) await sendRequest(port) - - const fileA = await variables.files[0] - if (!fileA.capacitor.closed) - await new Promise(resolve => fileA.capacitor.once('close', resolve)) - t.false(fs.existsSync(fileA.capacitor.path), 'Cleanup A.') - - const fileB = await variables.files[1] - if (!fileB.capacitor.closed) - await new Promise(resolve => fileB.capacitor.once('close', resolve)) - t.false(fs.existsSync(fileB.capacitor.path), 'Cleanup B.') }) }) @@ -1786,12 +1604,9 @@ t.test('Deprecated file upload ‘stream’ property.', async t => { } await t.test('Koa middleware.', async t => { - t.plan(2) - - let variables + t.plan(1) const app = new Koa().use(graphqlUploadKoa()).use(async (ctx, next) => { - ;({ variables } = ctx.request.body) await t.test('Upload.', uploadTest(ctx.request.body.variables.file)) ctx.status = 204 @@ -1801,23 +1616,15 @@ t.test('Deprecated file upload ‘stream’ property.', async t => { const port = await startServer(t, app) await sendRequest(port) - - const file = await variables.file - if (!file.capacitor.closed) - await new Promise(resolve => file.capacitor.once('close', resolve)) - t.false(fs.existsSync(file.capacitor.path), 'Cleanup.') }) await t.test('Express middleware.', async t => { - t.plan(2) - - let variables + t.plan(1) const app = express() .use(graphqlUploadExpress()) .use( expressAsyncHandler(async (request, response, next) => { - ;({ variables } = request.body) await t.test('Upload.', uploadTest(request.body.variables.file)) next() }) @@ -1826,10 +1633,5 @@ t.test('Deprecated file upload ‘stream’ property.', async t => { const port = await startServer(t, app) await sendRequest(port) - - const file = await variables.file - if (!file.capacitor.closed) - await new Promise(resolve => file.capacitor.once('close', resolve)) - t.false(fs.existsSync(file.capacitor.path), 'Cleanup.') }) })