Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
14 changes: 14 additions & 0 deletions test/app.listen.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,20 @@ describe('app.listen()', function(){
const server = app.listen();
server.close(done);
});

it('should not invoke callback more than once', function (done) {
var app = express()
var count = 0
var server = app.listen(0, function () {
count++
server.emit('error', new Error('boom'))
setImmediate(function () {
assert.strictEqual(count, 1)
server.close(done)
})
})
})

it('server.address() gives a { address, port, family } object', function (done) {
const app = express();
const server = app.listen(0, () => {
Expand Down
16 changes: 16 additions & 0 deletions test/req.is.js
Original file line number Diff line number Diff line change
Expand Up @@ -166,4 +166,20 @@ describe('req.is()', function () {
.expect(200, '"application/json"', done)
})
})

describe('when given an array of types', function () {
it('should not flatten the argument', function (done) {
var app = express()

app.use(function (req, res) {
res.json(req.is(['text/*', 'application/json']))
})

request(app)
.post('/')
.type('application/json')
.send('{}')
.expect(200, '"application/json"', done)
})
})
})
16 changes: 16 additions & 0 deletions test/res.append.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,22 @@ describe('res', function () {
.end(done)
})

it('should append an array to an existing single value', function (done) {
var app = express()

app.use(function (req, res) {
res.set('Set-Cookie', 'foo=bar')
res.append('Set-Cookie', ['fizz=buzz', 'pet=tobi'])
res.end()
})

request(app)
.get('/')
.expect(200)
.expect(shouldHaveHeaderValues('Set-Cookie', ['foo=bar', 'fizz=buzz', 'pet=tobi']))
.end(done)
})

it('should work together with res.cookie', function (done) {
var app = express()

Expand Down
22 changes: 22 additions & 0 deletions test/res.jsonp.js
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,28 @@ describe('res', function(){
.expect('Content-Type', 'text/javascript; charset=utf-8')
.expect(200, /cb\(42\)/, done)
})

it('should handle non-string stringify return values', function (done) {
var app = express()

app.use(function (req, res) {
var original = JSON.stringify
JSON.stringify = function () {
return 42
}

try {
res.jsonp('ignored')
} finally {
JSON.stringify = original
}
})

request(app)
.get('/?callback=cb')
.expect('Content-Type', 'text/javascript; charset=utf-8')
.expect(200, /cb\(42\)/, done)
})
})

describe('when given an array', function () {
Expand Down
42 changes: 42 additions & 0 deletions test/res.redirect.js
Original file line number Diff line number Diff line change
Expand Up @@ -211,4 +211,46 @@ describe('res', function(){
.end(done)
})
})

describe('when given invalid arguments', function () {
it('should handle missing url argument', function (done) {
var app = express()

app.use(function (req, res) {
res.redirect('')
})

request(app)
.get('/')
.expect(302)
.expect('Location', '')
.end(done)
})

it('should coerce when url is not a string', function (done) {
var app = express()

app.use(function (req, res) {
res.redirect(42)
})

request(app)
.get('/')
.expect(302)
.expect('Location', '42')
.end(done)
})

it('should fail when status is not a number', function (done) {
var app = express()

app.use(function (req, res) {
res.redirect('302', '/somewhere')
})

request(app)
.get('/')
.expect(500, done)
})
})
})
168 changes: 168 additions & 0 deletions test/res.sendFile.guards.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
'use strict'

var assert = require('node:assert')
var EventEmitter = require('node:events').EventEmitter
var Module = require('node:module')

var responseModulePath = require.resolve('../lib/response')

function loadMockedResponse(sendMock, onFinishedMock) {
var originalLoad = Module._load
var savedCache = require.cache[responseModulePath]

delete require.cache[responseModulePath]

Module._load = function (request, parent, isMain) {
if (request === 'send') return sendMock
if (request === 'on-finished') return onFinishedMock
return originalLoad.apply(this, arguments)
}

var mocked = require('../lib/response')

Module._load = originalLoad

if (savedCache) {
require.cache[responseModulePath] = savedCache
} else {
delete require.cache[responseModulePath]
}

return mocked
}

function createMockRes(response) {
var res = Object.create(response)
res.req = {
next: function () {}
}
res.app = {
enabled: function () {
return true
}
}
res.setHeader = function () {}
return res
}

function runScenario(setup, done) {
var callbackCount = 0
var callbackErr
var file

function sendMock() {
file = new EventEmitter()
file.pipe = function (res) {
setup.pipe(file, res)
}
return file
}

function onFinishedMock(res, onfinish) {
setup.onFinished(file, onfinish)
}

var response = loadMockedResponse(sendMock, onFinishedMock)
var res = createMockRes(response)

response.sendFile.call(res, '/tmp/fake.txt', function (err) {
callbackCount++
callbackErr = err || null
setup.onCallback && setup.onCallback(err)
})

setImmediate(function () {
setup.assert(callbackCount, callbackErr)
done()
})
}

describe('res.sendFile() internal guards', function () {
it('should ignore ECONNRESET finish after already ended (onaborted done-guard)', function (done) {
runScenario({
pipe: function (file) {
file.emit('end')
},
onFinished: function (file, onfinish) {
setImmediate(function () {
onfinish({ code: 'ECONNRESET' })
})
},
assert: function (count, err) {
assert.strictEqual(count, 1)
assert.strictEqual(err, null)
}
}, done)
})

it('should ignore directory event after error (ondirectory done-guard)', function (done) {
runScenario({
pipe: function (file) {
file.emit('error', new Error('boom'))
file.emit('directory')
},
onFinished: function () {},
assert: function (count, err) {
assert.strictEqual(count, 1)
assert.ok(err)
}
}, done)
})

it('should ignore error event after end (onerror done-guard)', function (done) {
runScenario({
pipe: function (file) {
file.emit('end')
file.emit('error', new Error('late error'))
},
onFinished: function () {},
assert: function (count, err) {
assert.strictEqual(count, 1)
assert.strictEqual(err, null)
}
}, done)
})

it('should ignore end event after error (onend done-guard)', function (done) {
runScenario({
pipe: function (file) {
file.emit('error', new Error('boom'))
file.emit('end')
},
onFinished: function () {},
assert: function (count, err) {
assert.strictEqual(count, 1)
assert.ok(err)
}
}, done)
})

it('should handle non-ECONNRESET finish errors via onerror', function (done) {
runScenario({
pipe: function () {},
onFinished: function (file, onfinish) {
onfinish({ code: 'EPIPE' })
},
assert: function (count, err) {
assert.strictEqual(count, 1)
assert.ok(err)
assert.strictEqual(err.code, 'EPIPE')
}
}, done)
})

it('should skip finish completion when done before setImmediate', function (done) {
runScenario({
pipe: function (file) {
file.emit('end')
},
onFinished: function (file, onfinish) {
onfinish()
},
assert: function (count, err) {
assert.strictEqual(count, 1)
assert.strictEqual(err, null)
}
}, done)
})
})
Loading