Show sent/received cookie counts in Cookies tab

- Add getCookieCounts function to parse cookie headers and count
  individual cookies (not just headers)
- Deduplicates by cookie name using Sets
- Display as sent/received format like Headers tab
- Add showZero to CountBadge so 0/3 displays properly
- Add tests for getCookieCounts
This commit is contained in:
Gregory Schier
2026-01-11 07:20:01 -08:00
parent c41e173a63
commit 2a5587c128
3 changed files with 140 additions and 19 deletions

View File

@@ -0,0 +1,93 @@
import type { HttpResponseEvent } from '@yaakapp-internal/models';
import { describe, expect, test } from 'vitest';
import { getCookieCounts } from './model_util';
function makeEvent(
type: string,
name: string,
value: string,
): HttpResponseEvent {
return {
id: 'test',
model: 'http_response_event',
responseId: 'resp',
createdAt: Date.now(),
event: { type, name, value } as HttpResponseEvent['event'],
};
}
describe('getCookieCounts', () => {
test('returns zeros for undefined events', () => {
expect(getCookieCounts(undefined)).toEqual({ sent: 0, received: 0 });
});
test('returns zeros for empty events', () => {
expect(getCookieCounts([])).toEqual({ sent: 0, received: 0 });
});
test('counts single sent cookie', () => {
const events = [makeEvent('header_up', 'Cookie', 'session=abc123')];
expect(getCookieCounts(events)).toEqual({ sent: 1, received: 0 });
});
test('counts multiple sent cookies in one header', () => {
const events = [makeEvent('header_up', 'Cookie', 'a=1; b=2; c=3')];
expect(getCookieCounts(events)).toEqual({ sent: 3, received: 0 });
});
test('counts single received cookie', () => {
const events = [makeEvent('header_down', 'Set-Cookie', 'session=abc123; Path=/')];
expect(getCookieCounts(events)).toEqual({ sent: 0, received: 1 });
});
test('counts multiple received cookies from multiple headers', () => {
const events = [
makeEvent('header_down', 'Set-Cookie', 'a=1; Path=/'),
makeEvent('header_down', 'Set-Cookie', 'b=2; HttpOnly'),
makeEvent('header_down', 'Set-Cookie', 'c=3; Secure'),
];
expect(getCookieCounts(events)).toEqual({ sent: 0, received: 3 });
});
test('deduplicates sent cookies by name', () => {
const events = [
makeEvent('header_up', 'Cookie', 'session=old'),
makeEvent('header_up', 'Cookie', 'session=new'),
];
expect(getCookieCounts(events)).toEqual({ sent: 1, received: 0 });
});
test('deduplicates received cookies by name', () => {
const events = [
makeEvent('header_down', 'Set-Cookie', 'token=abc; Path=/'),
makeEvent('header_down', 'Set-Cookie', 'token=xyz; Path=/'),
];
expect(getCookieCounts(events)).toEqual({ sent: 0, received: 1 });
});
test('counts both sent and received cookies', () => {
const events = [
makeEvent('header_up', 'Cookie', 'a=1; b=2; c=3'),
makeEvent('header_down', 'Set-Cookie', 'x=10; Path=/'),
makeEvent('header_down', 'Set-Cookie', 'y=20; Path=/'),
makeEvent('header_down', 'Set-Cookie', 'z=30; Path=/'),
];
expect(getCookieCounts(events)).toEqual({ sent: 3, received: 3 });
});
test('ignores non-cookie headers', () => {
const events = [
makeEvent('header_up', 'Content-Type', 'application/json'),
makeEvent('header_down', 'Content-Length', '123'),
];
expect(getCookieCounts(events)).toEqual({ sent: 0, received: 0 });
});
test('handles case-insensitive header names', () => {
const events = [
makeEvent('header_up', 'COOKIE', 'a=1'),
makeEvent('header_down', 'SET-COOKIE', 'b=2; Path=/'),
];
expect(getCookieCounts(events)).toEqual({ sent: 1, received: 1 });
});
});

View File

@@ -1,4 +1,10 @@
import type { AnyModel, Cookie, Environment, HttpResponseHeader } from '@yaakapp-internal/models';
import type {
AnyModel,
Cookie,
Environment,
HttpResponseEvent,
HttpResponseHeader,
} from '@yaakapp-internal/models';
import { getMimeTypeFromContentType } from './contentType';
export const BODY_TYPE_NONE = null;
@@ -59,3 +65,30 @@ export function isSubEnvironment(environment: Environment): boolean {
export function isFolderEnvironment(environment: Environment): boolean {
return environment.parentModel === 'folder';
}
export function getCookieCounts(
events: HttpResponseEvent[] | undefined,
): { sent: number; received: number } {
if (!events) return { sent: 0, received: 0 };
// Use Sets to deduplicate by cookie name
const sentNames = new Set<string>();
const receivedNames = new Set<string>();
for (const event of events) {
const e = event.event;
if (e.type === 'header_up' && e.name.toLowerCase() === 'cookie') {
// Parse "Cookie: name=value; name2=value2" format
for (const pair of e.value.split(';')) {
const name = pair.split('=')[0]?.trim();
if (name) sentNames.add(name);
}
} else if (e.type === 'header_down' && e.name.toLowerCase() === 'set-cookie') {
// Parse "Set-Cookie: name=value; ..." - first part before ; is name=value
const name = e.value.split(';')[0]?.split('=')[0]?.trim();
if (name) receivedNames.add(name);
}
}
return { sent: sentNames.size, received: receivedNames.size };
}