The following works:
describe('My App', function() {
describe('when logged in', function() {
it('should allow registered user to make a thing', function(done) {
agent.post('/make-a-thing')
.auth('testusername', 'validuserpass')
.send({thingName:'mythingname'})
.expect(201)
.end(function(err, res) {
if (err) return done(err);
res.body.should.have.property('thingUrl').and.to.match(/thing\/[0-9a-f]+$/);
done();
});
});
});
});
Now, if I want to add more and more tests to the "when logged in" block, I don't want to repeat the .auth('testusername', 'validuserpass') line every time. I should put the auth code in the beforeEach, because that's what beforeEach is for.
So I tried this:
describe("My App", function() {
describe('when logged out', function() {
it('should disallow anonymous user from doing things', function(done) {
agent.post('/do-things')
.send({thingName:'mythingname'})
.expect(403)
.end(function(err, res) {
if (err) return done(err);
done();
});
});
});
describe('when invalid user', function() {
beforeEach(function(done) {
agent.auth('invalidusername', 'invaliduserpass');
done();
});
it('should disallow unrecognized user from doing things', function(done) {
agent.post('/do-things')
.send({thingName:'mythingname'})
.expect(403)
.end(function(err, res) {
if (err) return done(err);
done();
});
});
})
describe('when logged in', function() {
beforeEach(function(done) {
agent.auth('testusername', 'validuserpass');
done();
});
it('should allow registered user to make a thing', function(done) {
agent.post('/make-a-thing')
.send({thingName:'mythingname'})
.expect(201)
.end(function(err, res) {
if (err) return done(err);
res.body.should.have.property('thingUrl').and.to.match(/thing\/[0-9a-f]+$/);
done();
});
});
it('should require name attribute to create a thing', function(done) {
agent.post('/make-a-thing')
.send({notaname:'notathingname'})
.expect(409)
.expect('Content-Type', /json/)
.end(function(err, res) {
if (err) return done(err);
done();
});
});
});
});
What happens is agent.auth is not defined. I think the auth method is defined in the result of auth.post.
Is there a way to do this?
For the record, here's what I did to solve this. I modified supertest's agent object and the Request prototype. agent now has a method called auth which causes Request.end to call auth first before ending, then reverts Request.end back to its original state.
configure.js
var app = require('app'),
supertest = require('supertest');
// global
agent = supertest.agent(app);
(function(Request) {
'use strict';
(function(_end) {
agent.auth = function() {
var authArgs = arguments;
Request.end = function() {
var endArgs = arguments;
var endResult = _end.apply(this.auth.apply(this, authArgs), endArgs);
Request.end = _end;
return endResult;
};
return agent;
};
})(Request.end);
})(agent.post('').constructor.prototype);
app-test.js
describe("My App", function() {
describe('when logged out', function() {
it('should disallow anonymous user from doing things', function(done) {
agent.post('/do-things')
.send({thingName:'mythingname'})
.expect(403)
.end(function(err, res) {
if (err) return done(err);
done();
});
});
});
describe('when invalid user', function() {
beforeEach(function(done) {
agent.auth('invalidusername', 'invaliduserpass');
done();
});
it('should disallow unrecognized user from doing things', function(done) {
agent.post('/do-things')
.send({thingName:'mythingname'})
.expect(403)
.end(function(err, res) {
if (err) return done(err);
done();
});
});
})
describe('when logged in', function() {
beforeEach(function(done) {
agent.auth('testusername', 'validuserpass');
done();
});
it('should allow registered user to make a thing', function(done) {
agent.post('/make-a-thing')
.send({thingName:'mythingname'})
.expect(201)
.end(function(err, res) {
if (err) return done(err);
res.body.should.have.property('thingUrl').and.to.match(/thing\/[0-9a-f]+$/);
done();
});
});
it('should require name attribute to create a thing', function(done) {
agent.post('/make-a-thing')
.send({notaname:'notathingname'})
.expect(409)
.expect('Content-Type', /json/)
.end(function(err, res) {
if (err) return done(err);
done();
});
});
});
});
My tests are run like this, so the configure script is executed first:
mocha tests/configure.js tests/*-test.js