I'm trying to use supertest's agent
function in a jest beforeEach()
to login the user before each test, as I want each test to run under the assumption that the user is signed in. For authentication, I am using passport and passport-local.
Test file:
import { agent, SuperAgentTest } from 'supertest';
import app from '../../src/app';
// create a `testRequest` variable to use in the tests
// that will be refreshed in between
let testRequest: SuperAgentTest;
const fakeUser = { email: 'john@john', username: 'john', password: 'john' };
beforeEach(async () => {
// create new agent
testRequest = agent(app);
// register and login
await testRequest.post('/register').send(fakeUser).expect(302);
// other irrelevant stuff...
});
// protected route
describe('POST /campgrounds/new', () => {
it('returns 200 OK', () => {
return testRequest.get('/campgrounds/new');
})
});
/register
route:
router.post('/register', async (req, res) => {
const { password, ...details } = req.body;
try {
// I am using passport-local-mongoose for this function-
// it just registers the user
const user = await User.register(new User(details), password);
req.login(user, (err) => {
// error handling and redirect
});
} catch (e) {
// error handling
}
})
Instead of a 200 status, I get a 302 status, meaning I was redirected to the login page. To debug this, I created a test route called /current
which will log the current user and session ID cookie. I then sent a GET
request to this route in both the it
and beforeEach
function respectively.
Interestingly, they both logged the same session ID, but only the request in beforeEach
had a user object attached to the request.
Make sure you have this before any routes or auth-related things.
app.use(express.json())
Ensure you call app.use(passport.initialize())
& app.use(passport.session())
before any app.use('/', aRouter)
, router.get
, router.post
, etc:
// Set up session w/ specific config
app.use(session({
secret: 'bquyqueajhbd',
resave: true,
saveUninitialized: true,
store: new FileStore({path: '/tmp/session'})
}));
// Wire up the
app.use(passport.initialize())
app.use(passport.session())
req.user
Passport is designed to store the user ID in session.
Every request to the server must reload the user from the database.
This is the job of the middleware passport.initialize()
and passport.session()
.
The logic there will call passport.deserializeUser
to lookup the user by ID - the same ID that was saved upon login into the session by passport.serializeUser
.
passport.serializeUser(function(user, done) {
done(null, user.id); // <-- Here's where the ID is saved to session.
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user); // <-- Here is where the `req.user` get's it's value from.
});
});
To debug this I'd focus on the
passport.deserializeUser
callback, add logs before and after the DB query.
(Note: it's been a few years since I taught this. Appologies if I'm not using the precise terms, etc.)