Skip to content

Commit

Permalink
Deprecates module-level configuration (#13)
Browse files Browse the repository at this point in the history
This change brings the supertest-session API closer in-line with the
`supertest` API it wraps. Dependents adopting the new API should create
sessions using the exported factory function:

    var request = require('supertest-session');

    request(myApp, myOpts)
      .get('/hello')
      ...

The API remains back-compatible for the time being, though legacy usage
should be considered deprecated and will be removed in the future.
  • Loading branch information
rjz committed Oct 3, 2015
1 parent d00d900 commit 4632fe9
Show file tree
Hide file tree
Showing 5 changed files with 181 additions and 83 deletions.
33 changes: 10 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,44 +21,32 @@ References:

Require `supertest-session` and pass in the test application:

var Session = require('supertest-session')({
app: require('../../path/to/app')
});

You can set environmental variables by including an `envs` object:

var Session = require('supertest-session')({
app: require('../../path/to/app'),
envs: { NODE_ENV: 'development' }
});

Set up a session:
var session = require('supertest-session');
var myApp = require('../../path/to/app');

before(function () {
this.sess = new Session();
});
var testSession = null;

after(function () {
this.sess.destroy();
beforeEach(function () {
testSession = session(myApp);
});

And set some expectations:

it('should fail accessing a restricted page', function (done) {
this.sess.get('/restricted')
testSession.get('/restricted')
.expect(401)
.end(done)
});

it('should sign in', function (done) {
this.sess.post('/signin')
testSession.post('/signin')
.send({ username: 'foo', password: 'password' })
.expect(200)
.end(done);
});

it('should get a restricted page', function (done) {
this.sess.get('/restricted')
testSession.get('/restricted')
.expect(200)
.end(done)
});
Expand All @@ -68,7 +56,7 @@ And set some expectations:
The cookies attached to the session may be retrieved from `session.cookies` at any time, for instance to inspect the contents of the current session in an external store.

it('should set session details correctly', function (done) {
var sessionCookie = _.find(sess.cookies, function (cookie) {
var sessionCookie = _.find(testSession.cookies, function (cookie) {
return _.has(cookie, 'connect.sid');
});

Expand All @@ -84,8 +72,7 @@ By default, supertest-session authenticates using session cookies. If your app
uses a custom strategy to restore sessions, you can provide `before` and `after`
hooks to adjust the request and inspect the response:

var Session = require('supertest-session')({
app: require('../../path/to/app'),
var testSession = session(myApp, {
before: function (req) {
req.set('authorization', 'Basic aGVsbG86d29ybGQK');
}
Expand Down
124 changes: 78 additions & 46 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
var cookie = require('cookie'),
var assign = require('object-assign'),
cookie = require('cookie'),
methods = require('methods'),
request = require('supertest'),
util = require('util');
Expand All @@ -23,67 +24,98 @@ function serializeCookie (c) {
}, []);
}

module.exports = function (config) {
function Session (app, options) {

if (!config) config = {};
if (!app) {
throw new Error('Session requires an `app`');
}

function Session () {
this.app = config.app;
this.app = app;
this.options = options || {};

if (this.options.helpers instanceof Object) {
assign(this, this.options.helpers);
}
}

Session.prototype._before = function (req) {
if (this.cookies) {
req.cookies = this.cookies.map(serializeCookie).join('; ');
}
if (config.before) config.before.call(this, req);
};
Session.prototype._before = function (req) {
if (this.cookies) {
req.cookies = this.cookies.map(serializeCookie).join('; ');
}

// Extract cookies once request is complete
Session.prototype._after = function (req, res) {
if (config.after) config.after.call(this, req, res);
if (res.headers.hasOwnProperty('set-cookie')) {
this.cookies = res.headers['set-cookie'].map(cookie.parse);
}
if (this.options.before) {
this.options.before.call(this, req);
}
};

// Extract cookies once request is complete
Session.prototype._after = function (req, res) {
if (this.options.after) {
this.options.after.call(this, req, res);
}

if (res.headers.hasOwnProperty('set-cookie')) {
this.cookies = res.headers['set-cookie'].map(cookie.parse);
}
};

Session.prototype.destroy = function () {
if (this.options.destroy) {
this.options.destroy.call(this);
}
this.cookies = null;
};

Session.prototype.request = function (meth, route) {
var req = request(this.app)[meth](route);
var sess = this;
var _end = req.end.bind(req);

this._before(req);

req.end = function (callback) {
return _end(function (err, res) {
if (err === null) sess._after(req, res);
return callback(err, res);
});
};

Session.prototype.destroy = function () {
if (config.destroy) config.destroy.call(this);
this.cookies = null;
return req;
};

methods.forEach(function (m) {
Session.prototype[m] = function () {
var args = [].slice.call(arguments);
return this.request.apply(this, [m].concat(args));
};
});

Session.prototype.request = function (meth, route) {
var req = request(this.app)[meth](route);
var sess = this;
var _end = req.end.bind(req);
Session.prototype.del = util.deprecate(Session.prototype.delete,
'supertest-session: Session.del is deprecated; please use Session.delete');

this._before(req);
function legacySession (config) {

req.end = function (callback) {
return _end(function (err, res) {
if (err === null) sess._after(req, res);
return callback(err, res);
});
};
if (!config) config = {};

return req;
};
// Bind session to `config`
function LegacySession () {
Session.call(this, config.app, config);
}

methods.forEach(function (m) {
Session.prototype[m] = function () {
var args = [].slice.call(arguments);
return this.request.apply(this, [m].concat(args));
};
});
util.inherits(LegacySession, Session);
assign(LegacySession.prototype, {}, config.helpers);

Session.prototype.del = util.deprecate(Session.prototype.delete,
'Session.del is deprecated; please use Session.delete');
return LegacySession;
}

if (config.helpers instanceof Object) {
Object.keys(config.helpers).forEach(function (key) {
Session.prototype[key] = config.helpers[key];
});
var deprecatedLegacySession = util.deprecate(legacySession,
'supertest-session: module configuration will be removed in next version.');

module.exports = function (app, options) {
if (!(app.listen instanceof Function)) {
return deprecatedLegacySession(app);
}

return Session;
return new Session(app, options);
};

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"dependencies": {
"cookie": "^0.1.3",
"methods": "^1.1.1",
"object-assign": "^4.0.1",
"supertest": "^1.0.1"
},
"devDependencies": {
Expand Down
84 changes: 84 additions & 0 deletions test/compatibility_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
var assert = require('assert'),
app = require('./app'),
session = require('../index');

describe('legacy configuration', function () {

describe('supertest session', function () {

var sess = null;

var Session = session({
app: app
});

beforeEach(function (done) {
sess = new Session();
sess.request('get', '/')
.expect(200)
.expect('GET,,1')
.end(done);
});

it('should increment session counter', function (done) {
sess.request('get', '/')
.expect(200)
.expect('GET,,2')
.end(done);
});

it('should destroy session', function (done) {
sess.destroy();
sess.get('/')
.expect(200)
.expect('GET,,1')
.end(done);
});

describe('method sugar', function () {
var methods = {
'del' : 'DELETE',
'get' : 'GET',
'post' : 'POST',
'put' : 'PUT',
'patch' : 'PATCH'
};

Object.keys(methods).forEach(function (key) {
it('should support ' + key, function (done) {
sess[key]('/')
.expect(200)
.expect(methods[key] + ',,2')
.end(done);
});
});
});
});

describe('Session with a .before hook', function () {

var sess = null;

var Session = session({
app: app,
before: function (req) {
req.set('authorization', 'bearer TEST_SESSION_TOKEN');
}
});

beforeEach(function (done) {
sess = new Session();
sess.request('get', '/token')
.expect(200)
.expect('GET,token,1')
.end(done);
});

it('should increment session counter', function (done) {
sess.request('get', '/token')
.expect(200)
.expect('GET,token,2')
.end(done);
});
});
});
22 changes: 8 additions & 14 deletions test/main_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,12 @@ var assert = require('assert'),
app = require('./app'),
session = require('../index');

describe('supertest session', function () {
describe('supertest-session', function () {

var sess = null;

var Session = session({
app: app
});

beforeEach(function (done) {
sess = new Session();
sess = session(app);
sess.request('get', '/')
.expect(200)
.expect('GET,,1')
Expand Down Expand Up @@ -57,15 +53,13 @@ describe('Session with a .before hook', function () {

var sess = null;

var Session = session({
app: app,
before: function (req) {
req.set('authorization', 'bearer TEST_SESSION_TOKEN');
}
});

beforeEach(function (done) {
sess = new Session();
sess = session(app, {
before: function (req) {
req.set('authorization', 'bearer TEST_SESSION_TOKEN');
}
});

sess.request('get', '/token')
.expect(200)
.expect('GET,token,1')
Expand Down

0 comments on commit 4632fe9

Please sign in to comment.