Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/unit-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [14.x, 16.x, 18.x, 20.x]
node-version: [18.x, 20.x, 22.x]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
Expand Down
27 changes: 14 additions & 13 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,29 +28,30 @@
}
],
"devDependencies": {
"@commitlint/cli": "^17.3.0",
"@commitlint/config-conventional": "^17.3.0",
"@koa/router": "^12",
"@commitlint/cli": "^19.8.1",
"@commitlint/config-conventional": "^19.8.1",
"@koa/router": "^13",
"concat-stream": "^2",
"eslint-config-xo-lass": "^2.0.1",
"fix-esm": "^1.0.1",
"fixpack": "^4.0.0",
"form-data": "^4",
"fs-temp": "^1",
"husky": "^8.0.2",
"koa": "^2",
"lint-staged": "^13.0.3",
"mocha": "3.x",
"fs-temp": "^2",
"husky": "^9.1.7",
"koa": "^3",
"lint-staged": "^16.0.0",
"mocha": "^11.3.0",
"multer": "1",
"nyc": "^15.1.0",
"nyc": "^17.1.0",
"on-finished": "^2",
"remark-cli": "^11.0.0",
"remark-preset-github": "^4.0.4",
"rimraf": "^3",
"rimraf": "^6",
"testdata-w3c-json-form": "^1",
"xo": "^0.54.2"
"xo": "^0.60.0"
},
"engines": {
"node": ">= 14"
"node": ">= 18"
},
"files": [
"LICENSE",
Expand Down Expand Up @@ -113,7 +114,7 @@
},
"scripts": {
"lint": "xo && remark . -qfo",
"test": "npm run lint && mocha",
"test": "npm run lint && mocha --exit",
"test-ci": "nyc npm run test --reporter=lcov"
},
"xo": {
Expand Down
53 changes: 23 additions & 30 deletions test/_util.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const fs = require('node:fs');
const path = require('node:path');
const stream = require('node:stream');
const { promisify } = require('node:util');
const onFinished = require('on-finished');

exports.file = (name) => {
Expand All @@ -11,35 +12,27 @@ exports.fileSize = (path) => {
return fs.statSync(path).size;
};

exports.submitForm = (multer, form, cb) => {
form.getLength((err, length) => {
if (err) return cb(err);

const req = new stream.PassThrough();

req.complete = false;
form.once('end', () => {
req.complete = true;
});

form.pipe(req);
req.headers = {
'content-type': 'multipart/form-data; boundary=' + form.getBoundary(),
'content-length': length
};

const res = null;
const ctx = { req, res };
multer(ctx, () => {})
.then(() => {
onFinished(req, () => {
cb(null, req);
});
})
.catch((err_) => {
onFinished(req, () => {
cb(err_, req);
});
});
exports.submitForm = async (multer, form) => {
const getFormLength = promisify(form.getLength.bind(form));
const length = await getFormLength();

const req = new stream.PassThrough();

req.complete = false;
form.once('end', () => {
req.complete = true;
});

form.pipe(req);
req.headers = {
'content-type': 'multipart/form-data; boundary=' + form.getBoundary(),
'content-length': length
};

const res = null;
const ctx = { req, res };
await multer(ctx, () => {});
onFinished(req, () => {});

return req;
};
169 changes: 77 additions & 92 deletions test/disk-storage.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,52 +3,46 @@
const assert = require('node:assert');
const fs = require('node:fs');
const path = require('node:path');
const temp = require('fs-temp');
const rimraf = require('rimraf');
const { promisify } = require('node:util');
const temp = require('fix-esm').require('fs-temp').default;
const { rimraf } = require('rimraf');
const FormData = require('form-data');
const multer = require('..');
const util = require('./_util');

const tempMkdirAsync = promisify(temp.mkdir);

describe('Disk Storage', () => {
let uploadDir;
let upload;

beforeEach((done) => {
temp.mkdir((err, path) => {
if (err) return done(err);

uploadDir = path;
upload = multer({ dest: path });
done();
});
beforeEach(async () => {
uploadDir = await tempMkdirAsync();
upload = multer({ dest: uploadDir });
});

afterEach((done) => {
rimraf(uploadDir, done);
afterEach(async () => {
await rimraf(uploadDir);
});

it('should process parser/form-data POST request', (done) => {
it('should process parser/form-data POST request', async () => {
const form = new FormData();
const parser = upload.single('small0');

form.append('name', 'Multer');
form.append('small0', util.file('small0.dat'));

util.submitForm(parser, form, (err, req) => {
assert.ifError(err);

assert.equal(req.body.name, 'Multer');
const req = await util.submitForm(parser, form);

assert.equal(req.file.fieldname, 'small0');
assert.equal(req.file.originalname, 'small0.dat');
assert.equal(req.file.size, 1778);
assert.equal(util.fileSize(req.file.path), 1778);
assert.equal(req.body.name, 'Multer');

done();
});
assert.equal(req.file.fieldname, 'small0');
assert.equal(req.file.originalname, 'small0.dat');
assert.equal(req.file.size, 1778);
assert.equal(util.fileSize(req.file.path), 1778);
});

it('should process empty fields and an empty file', (done) => {
it('should process empty fields and an empty file', async () => {
const form = new FormData();
const parser = upload.single('empty');

Expand All @@ -63,27 +57,23 @@ describe('Disk Storage', () => {
form.append('checkboxempty', '');
form.append('checkboxempty', '');

util.submitForm(parser, form, (err, req) => {
assert.ifError(err);
const req = await util.submitForm(parser, form);

assert.equal(req.body.name, 'Multer');
assert.equal(req.body.version, '');
assert.equal(req.body.year, '');
assert.equal(req.body.name, 'Multer');
assert.equal(req.body.version, '');
assert.equal(req.body.year, '');

assert.deepEqual(req.body.checkboxfull, ['cb1', 'cb2']);
assert.deepEqual(req.body.checkboxhalfempty, ['cb1', '']);
assert.deepEqual(req.body.checkboxempty, ['', '']);
assert.deepEqual(req.body.checkboxfull, ['cb1', 'cb2']);
assert.deepEqual(req.body.checkboxhalfempty, ['cb1', '']);
assert.deepEqual(req.body.checkboxempty, ['', '']);

assert.equal(req.file.fieldname, 'empty');
assert.equal(req.file.originalname, 'empty.dat');
assert.equal(req.file.size, 0);
assert.equal(util.fileSize(req.file.path), 0);

done();
});
assert.equal(req.file.fieldname, 'empty');
assert.equal(req.file.originalname, 'empty.dat');
assert.equal(req.file.size, 0);
assert.equal(util.fileSize(req.file.path), 0);
});

it('should process multiple files', (done) => {
it('should process multiple files', async () => {
const form = new FormData();
const parser = upload.fields([
{ name: 'empty', maxCount: 1 },
Expand All @@ -103,70 +93,65 @@ describe('Disk Storage', () => {
form.append('medium', util.file('medium.dat'));
form.append('large', util.file('large.jpg'));

util.submitForm(parser, form, (err, req) => {
assert.ifError(err);

assert.deepEqual(req.body, {});

assert.equal(req.files.empty[0].fieldname, 'empty');
assert.equal(req.files.empty[0].originalname, 'empty.dat');
assert.equal(req.files.empty[0].size, 0);
assert.equal(util.fileSize(req.files.empty[0].path), 0);

assert.equal(req.files.tiny0[0].fieldname, 'tiny0');
assert.equal(req.files.tiny0[0].originalname, 'tiny0.dat');
assert.equal(req.files.tiny0[0].size, 122);
assert.equal(util.fileSize(req.files.tiny0[0].path), 122);

assert.equal(req.files.tiny1[0].fieldname, 'tiny1');
assert.equal(req.files.tiny1[0].originalname, 'tiny1.dat');
assert.equal(req.files.tiny1[0].size, 7);
assert.equal(util.fileSize(req.files.tiny1[0].path), 7);

assert.equal(req.files.small0[0].fieldname, 'small0');
assert.equal(req.files.small0[0].originalname, 'small0.dat');
assert.equal(req.files.small0[0].size, 1778);
assert.equal(util.fileSize(req.files.small0[0].path), 1778);

assert.equal(req.files.small1[0].fieldname, 'small1');
assert.equal(req.files.small1[0].originalname, 'small1.dat');
assert.equal(req.files.small1[0].size, 315);
assert.equal(util.fileSize(req.files.small1[0].path), 315);

assert.equal(req.files.medium[0].fieldname, 'medium');
assert.equal(req.files.medium[0].originalname, 'medium.dat');
assert.equal(req.files.medium[0].size, 13196);
assert.equal(util.fileSize(req.files.medium[0].path), 13196);

assert.equal(req.files.large[0].fieldname, 'large');
assert.equal(req.files.large[0].originalname, 'large.jpg');
assert.equal(req.files.large[0].size, 2413677);
assert.equal(util.fileSize(req.files.large[0].path), 2413677);

done();
});
const req = await util.submitForm(parser, form);
assert.deepEqual(req.body, {});

assert.equal(req.files.empty[0].fieldname, 'empty');
assert.equal(req.files.empty[0].originalname, 'empty.dat');
assert.equal(req.files.empty[0].size, 0);
assert.equal(util.fileSize(req.files.empty[0].path), 0);

assert.equal(req.files.tiny0[0].fieldname, 'tiny0');
assert.equal(req.files.tiny0[0].originalname, 'tiny0.dat');
assert.equal(req.files.tiny0[0].size, 122);
assert.equal(util.fileSize(req.files.tiny0[0].path), 122);

assert.equal(req.files.tiny1[0].fieldname, 'tiny1');
assert.equal(req.files.tiny1[0].originalname, 'tiny1.dat');
assert.equal(req.files.tiny1[0].size, 7);
assert.equal(util.fileSize(req.files.tiny1[0].path), 7);

assert.equal(req.files.small0[0].fieldname, 'small0');
assert.equal(req.files.small0[0].originalname, 'small0.dat');
assert.equal(req.files.small0[0].size, 1778);
assert.equal(util.fileSize(req.files.small0[0].path), 1778);

assert.equal(req.files.small1[0].fieldname, 'small1');
assert.equal(req.files.small1[0].originalname, 'small1.dat');
assert.equal(req.files.small1[0].size, 315);
assert.equal(util.fileSize(req.files.small1[0].path), 315);

assert.equal(req.files.medium[0].fieldname, 'medium');
assert.equal(req.files.medium[0].originalname, 'medium.dat');
assert.equal(req.files.medium[0].size, 13196);
assert.equal(util.fileSize(req.files.medium[0].path), 13196);

assert.equal(req.files.large[0].fieldname, 'large');
assert.equal(req.files.large[0].originalname, 'large.jpg');
assert.equal(req.files.large[0].size, 2413677);
assert.equal(util.fileSize(req.files.large[0].path), 2413677);
});

it('should remove uploaded files on error', (done) => {
it('should remove uploaded files on error', async () => {
const form = new FormData();
const parser = upload.single('tiny0');

form.append('tiny0', util.file('tiny0.dat'));
form.append('small0', util.file('small0.dat'));

util.submitForm(parser, form, (err, req) => {
try {
await util.submitForm(parser, form);
} catch (err) {
assert.equal(err.code, 'LIMIT_UNEXPECTED_FILE');
assert.equal(err.field, 'small0');
assert.deepEqual(err.storageErrors, []);

const files = fs.readdirSync(uploadDir);
assert.deepEqual(files, []);

done();
});
}
});

it("should report error when directory doesn't exist", (done) => {
it("should report error when directory doesn't exist", async () => {
const directory = path.join(temp.mkdirSync(), 'ghost');
function dest($0, $1, cb) {
cb(null, directory);
Expand All @@ -179,11 +164,11 @@ describe('Disk Storage', () => {

form.append('tiny0', util.file('tiny0.dat'));

util.submitForm(parser, form, (err, req) => {
try {
await util.submitForm(parser, form);
} catch (err) {
assert.equal(err.code, 'ENOENT');
assert.equal(path.dirname(err.path), directory);

done();
});
}
});
});
Loading