diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b9487c10..20efe66c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,21 +1,36 @@ name: ci on: -- pull_request -- push + push: + branches: + - main + paths-ignore: + - '*.md' + pull_request: permissions: contents: read - jobs: - test: - permissions: - checks: write # for coverallsapp/github-action to create new checks - contents: read # for actions/checkout to fetch code + lint: + name: Lint runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 'lts/*' + + - name: Install dependencies + run: npm install --ignore-scripts --include=dev + + - name: Run lint + run: npm run lint + test: strategy: fail-fast: false matrix: + os: [ubuntu-latest, windows-latest] name: - Node.js 10.x - Node.js 11.x @@ -35,7 +50,7 @@ jobs: include: - name: Node.js 10.x - node-version: "10.24" + node-version: "10.16.0" npm-i: mocha@8.4.0 - name: Node.js 11.x @@ -43,7 +58,7 @@ jobs: npm-i: mocha@8.4.0 - name: Node.js 12.x - node-version: "12.22" + node-version: "12.0.0" npm-i: mocha@9.2.2 - name: Node.js 13.x @@ -51,27 +66,27 @@ jobs: npm-i: mocha@9.2.2 - name: Node.js 14.x - node-version: "14.21" + node-version: "14.0.0" npm-i: mocha@9.2.2 - name: Node.js 15.x - node-version: "15.14" + node-version: "15.0.0" npm-i: mocha@9.2.2 - name: Node.js 16.x - node-version: "16.20" + node-version: "16.0.0" - name: Node.js 17.x - node-version: "17.9" + node-version: "17.0.0" - name: Node.js 18.x - node-version: "18.18" + node-version: "18.0.0" - name: Node.js 19.x - node-version: "19.9" + node-version: "19.0.0" - name: Node.js 20.x - node-version: "20.9" + node-version: "20.0.0" - name: Node.js 21.x node-version: "21" @@ -85,26 +100,16 @@ jobs: - name: Node.js 24.x node-version: "24" + runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - - name: Install Node.js ${{ matrix.node-version }} - shell: bash -eo pipefail -l {0} - run: | - nvm install --default ${{ matrix.node-version }} - if [[ "${{ matrix.node-version }}" == 0.* && "$(cut -d. -f2 <<< "${{ matrix.node-version }}")" -lt 10 ]]; then - nvm install --alias=npm 0.10 - nvm use ${{ matrix.node-version }} - if [[ "$(npm -v)" == 1.1.* ]]; then - nvm exec npm npm install -g npm@1.1 - ln -fs "$(which npm)" "$(dirname "$(nvm which npm)")/npm" - else - sed -i '1s;^.*$;'"$(printf '#!%q' "$(nvm which npm)")"';' "$(readlink -f "$(which npm)")" - fi - npm config set strict-ssl false - fi - dirname "$(nvm which ${{ matrix.node-version }})" >> "$GITHUB_PATH" + - uses: actions/checkout@v4 + with: + persist-credentials: false + - name: Setup Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} - name: Remove npm module(s) ${{ matrix.npm-rm }} run: npm rm --silent --save-dev ${{ matrix.npm-rm }} if: matrix.npm-rm != '' @@ -116,43 +121,41 @@ jobs: - name: Install Node.js dependencies run: npm install - - name: List environment - id: list_env - shell: bash - run: | - echo "node@$(node -v)" - echo "npm@$(npm -v)" - npm -s ls ||: - (npm -s ls --depth=0 ||:) | awk -F'[ @]' 'NR>1 && $2 { print $2 "=" $3 }' >> "$GITHUB_OUTPUT" - - - name: Lint code - run: npm run lint - - name: Run tests shell: bash - run: | - if npm -ps ls nyc | grep -q nyc; then - npm run test-ci - else - npm test - fi + run: npm run test-ci - - name: Collect code coverage - uses: coverallsapp/github-action@09b709cf6a16e30b0808ba050c7a6e8a5ef13f8d # master - if: steps.list_env.outputs.nyc != '' + - name: Upload code coverage + uses: actions/upload-artifact@v4 with: - github-token: ${{ secrets.GITHUB_TOKEN }} - flag-name: run-${{ matrix.test_number }} - parallel: true + name: coverage-node-${{ matrix.node-version }}-${{ matrix.os }} + path: ./coverage/lcov.info + retention-days: 1 coverage: - permissions: - checks: write # for coverallsapp/github-action to create new checks - needs: test - runs-on: ubuntu-latest - steps: - - name: Upload code coverage - uses: coverallsapp/github-action@09b709cf6a16e30b0808ba050c7a6e8a5ef13f8d # master - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - parallel-finished: true + needs: test + runs-on: ubuntu-latest + permissions: + contents: read + checks: write + steps: + - uses: actions/checkout@v4 + + - name: Install lcov + shell: bash + run: sudo apt-get -y install lcov + + - name: Collect coverage reports + uses: actions/download-artifact@v4 + with: + path: ./coverage + pattern: coverage-node-* + + - name: Merge coverage reports + shell: bash + run: find ./coverage -name lcov.info -exec printf '-a %q\n' {} \; | xargs lcov -o ./lcov.info + + - name: Upload coverage report + uses: coverallsapp/github-action@v2 + with: + file: ./lcov.info diff --git a/test/_util.js b/test/_util.js index a6782ad8..d892f427 100644 --- a/test/_util.js +++ b/test/_util.js @@ -2,14 +2,26 @@ var fs = require('fs') var path = require('path') var stream = require('stream') +exports.filePath = function filePath (name) { + return path.join(__dirname, 'files', name) +} + exports.file = function file (name) { - return fs.createReadStream(path.join(__dirname, 'files', name)) + return fs.createReadStream(this.filePath(name)) } exports.fileSize = function fileSize (path) { return fs.statSync(path).size } +exports.fileSizeByName = function fileSizeByName (name) { + return this.fileSize(this.filePath(name)) +} + +exports.readDir = function readDir (dirPath) { + return fs.readdirSync(dirPath) +} + exports.submitForm = function submitForm (multer, form, cb) { form.getLength(function (err, length) { if (err) return cb(err) diff --git a/test/disk-storage.js b/test/disk-storage.js index bc923ab5..23454bf8 100644 --- a/test/disk-storage.js +++ b/test/disk-storage.js @@ -3,7 +3,6 @@ var assert = require('assert') var deepEqual = require('deep-equal') -var fs = require('fs') var path = require('path') var util = require('./_util') var multer = require('../') @@ -11,6 +10,14 @@ var temp = require('fs-temp') var rimraf = require('rimraf') var FormData = require('form-data') +function assertFileProperties (file, expectedFieldname, expectedOriginalname) { + const expectedSize = util.fileSizeByName(expectedOriginalname) + assert.strictEqual(file.fieldname, expectedFieldname) + assert.strictEqual(file.originalname, expectedOriginalname) + assert.strictEqual(file.size, expectedSize) + assert.strictEqual(util.fileSize(file.path), expectedSize) +} + describe('Disk Storage', function () { var uploadDir, upload @@ -40,10 +47,7 @@ describe('Disk Storage', function () { assert.strictEqual(req.body.name, 'Multer') - assert.strictEqual(req.file.fieldname, 'small0') - assert.strictEqual(req.file.originalname, 'small0.dat') - assert.strictEqual(req.file.size, 1778) - assert.strictEqual(util.fileSize(req.file.path), 1778) + assertFileProperties(req.file, 'small0', 'small0.dat') done() }) @@ -75,10 +79,7 @@ describe('Disk Storage', function () { assert(deepEqual(req.body.checkboxhalfempty, ['cb1', ''])) assert(deepEqual(req.body.checkboxempty, ['', ''])) - assert.strictEqual(req.file.fieldname, 'empty') - assert.strictEqual(req.file.originalname, 'empty.dat') - assert.strictEqual(req.file.size, 0) - assert.strictEqual(util.fileSize(req.file.path), 0) + assertFileProperties(req.file, 'empty', 'empty.dat') done() }) @@ -109,40 +110,13 @@ describe('Disk Storage', function () { assert(deepEqual(req.body, {})) - assert.strictEqual(req.files.empty[0].fieldname, 'empty') - assert.strictEqual(req.files.empty[0].originalname, 'empty.dat') - assert.strictEqual(req.files.empty[0].size, 0) - assert.strictEqual(util.fileSize(req.files.empty[0].path), 0) - - assert.strictEqual(req.files.tiny0[0].fieldname, 'tiny0') - assert.strictEqual(req.files.tiny0[0].originalname, 'tiny0.dat') - assert.strictEqual(req.files.tiny0[0].size, 122) - assert.strictEqual(util.fileSize(req.files.tiny0[0].path), 122) - - assert.strictEqual(req.files.tiny1[0].fieldname, 'tiny1') - assert.strictEqual(req.files.tiny1[0].originalname, 'tiny1.dat') - assert.strictEqual(req.files.tiny1[0].size, 7) - assert.strictEqual(util.fileSize(req.files.tiny1[0].path), 7) - - assert.strictEqual(req.files.small0[0].fieldname, 'small0') - assert.strictEqual(req.files.small0[0].originalname, 'small0.dat') - assert.strictEqual(req.files.small0[0].size, 1778) - assert.strictEqual(util.fileSize(req.files.small0[0].path), 1778) - - assert.strictEqual(req.files.small1[0].fieldname, 'small1') - assert.strictEqual(req.files.small1[0].originalname, 'small1.dat') - assert.strictEqual(req.files.small1[0].size, 315) - assert.strictEqual(util.fileSize(req.files.small1[0].path), 315) - - assert.strictEqual(req.files.medium[0].fieldname, 'medium') - assert.strictEqual(req.files.medium[0].originalname, 'medium.dat') - assert.strictEqual(req.files.medium[0].size, 13196) - assert.strictEqual(util.fileSize(req.files.medium[0].path), 13196) - - assert.strictEqual(req.files.large[0].fieldname, 'large') - assert.strictEqual(req.files.large[0].originalname, 'large.jpg') - assert.strictEqual(req.files.large[0].size, 2413677) - assert.strictEqual(util.fileSize(req.files.large[0].path), 2413677) + assertFileProperties(req.files.empty[0], 'empty', 'empty.dat') + assertFileProperties(req.files.tiny0[0], 'tiny0', 'tiny0.dat') + assertFileProperties(req.files.tiny1[0], 'tiny1', 'tiny1.dat') + assertFileProperties(req.files.small0[0], 'small0', 'small0.dat') + assertFileProperties(req.files.small1[0], 'small1', 'small1.dat') + assertFileProperties(req.files.medium[0], 'medium', 'medium.dat') + assertFileProperties(req.files.large[0], 'large', 'large.jpg') done() }) @@ -160,8 +134,7 @@ describe('Disk Storage', function () { assert.strictEqual(err.field, 'small0') assert(deepEqual(err.storageErrors, [])) - var files = fs.readdirSync(uploadDir) - assert(deepEqual(files, [])) + assert(deepEqual(util.readDir(uploadDir), [])) done() }) @@ -185,4 +158,19 @@ describe('Disk Storage', function () { done() }) }) + + it('should handle case where fieldname and originalname did not match', function (done) { + var form = new FormData() + var parser = upload.single('profilePic') + + form.append('profilePic', util.file('large.jpg')) + + util.submitForm(parser, form, function (err, req) { + assert.ifError(err) + + assertFileProperties(req.file, 'profilePic', 'large.jpg') + + done() + }) + }) }) diff --git a/test/express-integration.js b/test/express-integration.js index ff1757b4..5d3b48fa 100644 --- a/test/express-integration.js +++ b/test/express-integration.js @@ -70,7 +70,9 @@ describe('Express Integration', function () { it('should work when receiving error from fileFilter', function (done) { function fileFilter (req, file, cb) { - cb(new Error('TEST')) + if (file.mimetype !== 'image/png') { + return cb(new Error('Only PNG files are allowed')) + } } var upload = multer({ fileFilter: fileFilter }) @@ -80,7 +82,7 @@ describe('Express Integration', function () { var routeCalled = 0 var errorCalled = 0 - form.append('avatar', util.file('large.jpg')) + form.append('avatar', util.file('small0.dat')) router.post('/profile', upload.single('avatar'), function (req, res, next) { routeCalled++ @@ -88,7 +90,7 @@ describe('Express Integration', function () { }) router.use(function (err, req, res, next) { - assert.strictEqual(err.message, 'TEST') + assert.strictEqual(err.message, 'Only PNG files are allowed') errorCalled++ res.status(500).end('ERROR') diff --git a/test/functionality.js b/test/functionality.js index a4db245a..0a9d42de 100644 --- a/test/functionality.js +++ b/test/functionality.js @@ -1,7 +1,7 @@ /* eslint-env mocha */ var assert = require('assert') - +var path = require('path') var util = require('./_util') var multer = require('../') var temp = require('fs-temp') @@ -52,7 +52,7 @@ describe('Functionality', function () { util.submitForm(parser, env.form, function (err, req) { assert.ifError(err) assert.ok(startsWith(req.file.path, env.uploadDir)) - assert.strictEqual(util.fileSize(req.file.path), 1778) + assert.strictEqual(util.fileSize(req.file.path), util.fileSizeByName('small0.dat')) done() }) }) @@ -128,9 +128,10 @@ describe('Functionality', function () { util.submitForm(parser, form, function (err, req) { assert.ifError(err) + assert.strictEqual(req.files.length, 2) - assert.ok(req.files[0].path.indexOf('/testforme-') >= 0) - assert.ok(req.files[1].path.indexOf('/testforme-') >= 0) + assert.ok(req.files[0].path.indexOf(`${path.sep}testforme-`) >= 0) + assert.ok(req.files[1].path.indexOf(`${path.sep}testforme-`) >= 0) done() }) }) diff --git a/test/memory-storage.js b/test/memory-storage.js index cbc3e2e5..4a1607dc 100644 --- a/test/memory-storage.js +++ b/test/memory-storage.js @@ -2,11 +2,19 @@ var assert = require('assert') var deepEqual = require('deep-equal') - var util = require('./_util') var multer = require('../') var FormData = require('form-data') +function assertFileProperties (file, expectedFieldname, expectedOriginalname) { + const expectedSize = util.fileSizeByName(expectedOriginalname) + assert.strictEqual(file.fieldname, expectedFieldname) + assert.strictEqual(file.originalname, expectedOriginalname) + assert.strictEqual(file.size, expectedSize) + assert.ok(Buffer.isBuffer(file.buffer)) + assert.strictEqual(file.buffer.length, expectedSize) +} + describe('Memory Storage', function () { var upload @@ -27,10 +35,7 @@ describe('Memory Storage', function () { assert.strictEqual(req.body.name, 'Multer') - assert.strictEqual(req.file.fieldname, 'small0') - assert.strictEqual(req.file.originalname, 'small0.dat') - assert.strictEqual(req.file.size, 1778) - assert.strictEqual(req.file.buffer.length, 1778) + assertFileProperties(req.file, 'small0', 'small0.dat') done() }) @@ -62,11 +67,7 @@ describe('Memory Storage', function () { assert(deepEqual(req.body.checkboxhalfempty, ['cb1', ''])) assert(deepEqual(req.body.checkboxempty, ['', ''])) - assert.strictEqual(req.file.fieldname, 'empty') - assert.strictEqual(req.file.originalname, 'empty.dat') - assert.strictEqual(req.file.size, 0) - assert.strictEqual(req.file.buffer.length, 0) - assert.strictEqual(Buffer.isBuffer(req.file.buffer), true) + assertFileProperties(req.file, 'empty', 'empty.dat') done() }) @@ -97,40 +98,28 @@ describe('Memory Storage', function () { assert(deepEqual(req.body, {})) - assert.strictEqual(req.files.empty[0].fieldname, 'empty') - assert.strictEqual(req.files.empty[0].originalname, 'empty.dat') - assert.strictEqual(req.files.empty[0].size, 0) - assert.strictEqual(req.files.empty[0].buffer.length, 0) - - assert.strictEqual(req.files.tiny0[0].fieldname, 'tiny0') - assert.strictEqual(req.files.tiny0[0].originalname, 'tiny0.dat') - assert.strictEqual(req.files.tiny0[0].size, 122) - assert.strictEqual(req.files.tiny0[0].buffer.length, 122) - - assert.strictEqual(req.files.tiny1[0].fieldname, 'tiny1') - assert.strictEqual(req.files.tiny1[0].originalname, 'tiny1.dat') - assert.strictEqual(req.files.tiny1[0].size, 7) - assert.strictEqual(req.files.tiny1[0].buffer.length, 7) - - assert.strictEqual(req.files.small0[0].fieldname, 'small0') - assert.strictEqual(req.files.small0[0].originalname, 'small0.dat') - assert.strictEqual(req.files.small0[0].size, 1778) - assert.strictEqual(req.files.small0[0].buffer.length, 1778) - - assert.strictEqual(req.files.small1[0].fieldname, 'small1') - assert.strictEqual(req.files.small1[0].originalname, 'small1.dat') - assert.strictEqual(req.files.small1[0].size, 315) - assert.strictEqual(req.files.small1[0].buffer.length, 315) - - assert.strictEqual(req.files.medium[0].fieldname, 'medium') - assert.strictEqual(req.files.medium[0].originalname, 'medium.dat') - assert.strictEqual(req.files.medium[0].size, 13196) - assert.strictEqual(req.files.medium[0].buffer.length, 13196) - - assert.strictEqual(req.files.large[0].fieldname, 'large') - assert.strictEqual(req.files.large[0].originalname, 'large.jpg') - assert.strictEqual(req.files.large[0].size, 2413677) - assert.strictEqual(req.files.large[0].buffer.length, 2413677) + assertFileProperties(req.files.empty[0], 'empty', 'empty.dat') + assertFileProperties(req.files.tiny0[0], 'tiny0', 'tiny0.dat') + assertFileProperties(req.files.tiny1[0], 'tiny1', 'tiny1.dat') + assertFileProperties(req.files.small0[0], 'small0', 'small0.dat') + assertFileProperties(req.files.small1[0], 'small1', 'small1.dat') + assertFileProperties(req.files.medium[0], 'medium', 'medium.dat') + assertFileProperties(req.files.large[0], 'large', 'large.jpg') + + done() + }) + }) + + it('should handle case where fieldname and originalname did not match', function (done) { + var form = new FormData() + var parser = upload.single('profilePic') + + form.append('profilePic', util.file('large.jpg')) + + util.submitForm(parser, form, function (err, req) { + assert.ifError(err) + + assertFileProperties(req.file, 'profilePic', 'large.jpg') done() }) diff --git a/test/reuse-middleware.js b/test/reuse-middleware.js index ab287041..7172ec58 100644 --- a/test/reuse-middleware.js +++ b/test/reuse-middleware.js @@ -18,6 +18,7 @@ describe('Reuse Middleware', function () { var pending = 8 function submitData (fileCount) { + var expectedSize = util.fileSizeByName('small0.dat') var form = new FormData() form.append('name', 'Multer') @@ -37,8 +38,8 @@ describe('Reuse Middleware', function () { req.files.forEach(function (file) { assert.strictEqual(file.fieldname, 'them-files') assert.strictEqual(file.originalname, 'small0.dat') - assert.strictEqual(file.size, 1778) - assert.strictEqual(file.buffer.length, 1778) + assert.strictEqual(file.size, expectedSize) + assert.strictEqual(file.buffer.length, expectedSize) }) if (--pending === 0) done()