supertest icon indicating copy to clipboard operation
supertest copied to clipboard

Response cookies could be parsed

Open nitrocode opened this issue 7 years ago • 5 comments

Thank you for the great package. I use it all the time in my tests.

I'm testing response cookies and I realized in order to get response cookies I need to do something like this

.expect(res => {
    console.log(res.headers['set-cookie'][0]);
});

And then dig through the set cookie header to verify that the correct cookie was returned. To make this easier, I converted the set-cookie header array to a dictionary using the cookie module

> const cookie = require('cookie');
> cookie.parse('cookieName=cookieValue; path=/; expires=Thu, 16 May 2019 19:00:43 GMT; domain=.google.co; httponly')
{
  cookieName: 'cookieValue',
  path: '/',
  expires: 'Thu, 16 May 2019 19:00:43 GMT',
  domain: '.google.co'
}

If this was part of supertest and could be used as an object off of the response object, it would be very helpful. I imagine a format something like this:

> res.cookies
{
  cookieName: { 
    value: 'cookieValue',
    path: '/',
    expires: 'Thu, 16 May 2019 19:00:43 GMT',
    domain: '.google.com'
  }, 
cookieName2: { 
    value: 'cookieValue2',
    path: '/',
    expires: 'Thu, 16 May 2019 19:00:43 GMT',
    domain: '.yahoo.com'
  }, 
}

What do you think? Good idea? Would you accept a PR for this?

nitrocode avatar May 16 '18 19:05 nitrocode

cookie module is broken because of misleading name. This module is capable to parse cookie header only and not set-cookie header. For example, you miss secure and httpOnly fields. For me, a module named cookie should do everything with cookies: create, verify, parse, modify, delete at least in browser and node.

See:

  • https://github.com/jshttp/cookie/issues/83#issuecomment-407904740,
  • https://github.com/jshttp/cookie/issues/72#issuecomment-359868195
  • https://github.com/jshttp/cookie/issues/70#issuecomment-327000167
  • and many others.

Shame on this module.

I use https://github.com/nfriedly/set-cookie-parser for my tests. Even it would be nice to get it built-in, I think this isn't required, because you get it very cheap:

agent
	.get('/test')
	.set('Accept', 'application/json')
	.expect((res: IncomingMessage) => {
		should(cookieParser.parse(res)
			// compare only important values, can be extracted in a utility function
			.map((c: Cookie) => {
				return {
				name: c.name,
				path: c.path,
				httpOnly: !!c.httpOnly,
				maxAge: c.maxAge,
				secure: c.secure
			}}))
			.deepEqual([
				{
					name: 'a',
					maxAge: 60,
					path: '/',
					httpOnly: true,
					secure: true
				},
				{
					name: 'b',
					maxAge: 60,
					path: '/',
					httpOnly: false,
					secure: true
				},
			])
	})
	.expect(204, done)

Bessonov avatar Aug 04 '18 17:08 Bessonov

@Bessonov I like your approach! I agree the cookie module isn't the best option. I'm sure there are other modules that do support the flags you mentioned.

Even with your helpful approach, it would be very nice to have the ability to go through a json dictionary of cookie names and their corresponding flags via res.cookies.

nitrocode avatar Aug 05 '18 18:08 nitrocode

Has there been any move towards this? I use supertest a lot and it would be a great feature

LuisOsta avatar May 30 '19 22:05 LuisOsta

@LuisOsta havent seen anything on this so i wrote a small parser (~15 lines). feel free to drop it into your codebase. heres the gist, it includes jest tests as well https://gist.github.com/the-vampiire/a564af41ed0ce8eb7c30dbe6c0f627d8

output shape

const cookies = {
  cookieName: {
    value: 'cookie value',
    flags: {
      flagName: 'flag value',
      booleanFlag: true, // boolean flags (no value) are given true as their value
    },
  },
};

usage

const app = require('./app');
const request = require('supertest');
const { extractCookies } = require('./utils/extract-cookies');

test('some test needing cookie details', async () => {
  const res = await request(app).post('/tokens', { ...data });
  const cookies = extractCookies(res.headers); // or res.header alias
  // do tests on cookies, cookies.cookieName.[value, flags.[flagName]]
});

the-vampiire avatar Jul 04 '19 02:07 the-vampiire

Getting cookies from the agent worked for me.

import { Cookie, CookieAccessInfo } from 'cookiejar';

it('Has expected cookie', async () => {
    const agent = request.agent(app);
    const response = await agent.post('/tokens');
    const someCookie:Cookie = agent.jar.getCookie('someCookieName', CookieAccessInfo.All);

    expect(someCookie).toBeDefined();
    expect(someCookie?.value).toBe('someTokenValue');
});

agent.jar.getCookie returns Cookie | undefined. Cookie type def includes: name, value, expiration_date...

There's also getCookies(accessInfo: CookieAccessInfo) if needed.

willrust avatar Jan 23 '23 22:01 willrust