Files
bruno/packages/bruno-js/tests/cookie-list.spec.js
sanish chirayath bef4b6bbee feat(cookies): add direct cookie access methods and update translations (#7073)
* feat(cookies): add direct cookie access methods and update translations

- Introduced new methods for direct cookie access: `bru.cookies.get`, `bru.cookies.has`, and `bru.cookies.toObject`.
- Updated translation mappings in `bruno-to-postman-translator` and `postman-to-bruno-translator` to support these new methods.
- Enhanced tests to verify correct translation between `bru` and `pm` cookie methods, including mixed usage scenarios.
- Updated `Bru` class to handle cookie access based on the current request URL.

* feat(cookies): enhance cookie management with new methods and refactor

- Added new cookie methods: `toString`, `clear`, `delete`, `one`, `all`, `idx`, `count`, `indexOf`, `find`, `filter`, `each`, `map`, and `reduce` to `bru.cookies`.
- Refactored `Bru` class to utilize a new `CookieList` for cookie management, improving structure and readability.
- Updated translation mappings in `bruno-to-postman-translator` and `postman-to-bruno-translator` to include new cookie methods.
- Introduced `PropertyList` and `ReadOnlyPropertyList` classes for better data structure management.
- Enhanced tests for comprehensive coverage of new cookie functionalities and their interactions.

* docs(readonly-property-list): clarify array usage in constructor comments

* feat(cookies): add direct cookie manipulation tests and methods

* feat(cookies): add hasCookie method for checking cookie existence

* fix

* refactor(cookies): simplify cookie method translations

* feat(cookies): expand cookie API with new methods and tests

- Added new cookie methods: `get`, `has`, `toString`, `clear`, `upsert`, `remove`, `idx`, and `indexOf` to enhance cookie management.
- Updated translation mappings for `bru.cookies` to include new methods in `bruno-to-postman-translator` and `postman-to-bruno-translator`.
- Introduced tests for new methods and their interactions, ensuring comprehensive coverage of cookie functionalities.
- Enhanced existing tests to validate correct behavior of cookie methods across different scenarios.

* refactor(cookies): update CookieList to extend PropertyList and improve error handling

* test(cookies): add regression tests for jar and direct cookie patterns

- Introduced regression tests to ensure that jar patterns are correctly prioritized over direct cookie access patterns in translations.
- Updated `CookieList` to extend `ReadOnlyPropertyList` instead of `PropertyList`, clarifying its functionality.
- Refactored cookie method handling in the `bru` shim to utilize a new asynchronous bridge for improved error handling and consistency.

* refactor(cookies): update translations and remove PropertyList

- Enhanced comments in `postman-translations.js` to clarify the order of cookie jar translations.
- Updated `cookie-list.js` comments to better describe the factory function for the cookie jar.
- Removed the `PropertyList` class and its associated tests, streamlining the codebase and focusing on `ReadOnlyPropertyList` and `CookieList` for cookie management.

* fix(cookies): normalize tough-cookie objects and improve remove method comments

- Updated `CookieList` to normalize tough-cookie instances to plain objects, preventing circular references and exposing internal structures.
- Enhanced comments in the `remove` method to clarify behavior when removing non-existent or empty-named cookies.

* test(cookies): update tests to use async/await for consistency

* test(cookies): use async/await in cookie tests for consistency

* refactor(readonly-property-list): update get and reduce methods for improved behavior

* fix(cookies): update cookie method signature in autocomplete hints and enhance translation comments

- Modified the autocomplete hint for `bru.cookies.has` to include the new signature with an optional value parameter.
- Improved comments in `postman-translations.js` to clarify the order of cookie jar translation patterns for better understanding.

* refactor(cookies): introduce PropertyList for enhanced cookie management

* refactor(property-list): simplify repopulate method and enhance item handling logic

* feat(cookies): implement PropertyList bridge for enhanced cookie management

- Introduced a new `createPropertyListBridge` utility to streamline the integration of cookie methods into the QuickJS VM.
- Replaced the previous async cookie bridge with a more flexible approach, allowing for both synchronous and asynchronous cookie operations.
- Added comprehensive tests to validate the functionality of the new cookie methods in both developer and safe modes.
- Updated existing cookie tests to ensure compatibility with the new PropertyList structure.

* fix(tests): correct expected passed requests in cookie tests

- Updated the expected number of passed requests in the cookie tests from 34 to 6 to reflect the correct validation results.
- Ensured consistency in test assertions across multiple test cases for the PropertyList API.

* fix(cookies): update cookie URLs to use localhost for testing

- Changed all cookie-related test scripts to use `{{localhost}}` instead of `{{host}}` for the ping URL, ensuring consistency in local testing environments.
- Updated the cookie test suite to reflect the new URL structure, enhancing the reliability of the tests.
- Removed outdated cookie test files to streamline the test suite.

* refactor(cookies): standardize cookie handling with localhost variable

- Updated cookie test scripts to utilize the `{{localhost}}` variable for setting and retrieving cookies, ensuring consistency across tests.
- Enhanced clarity in comments regarding cookie behavior for different domains.
- Improved test assertions to validate cookie management functionality more effectively.

* refactor(property-list, readonly-property-list): update methods to use private class fields

* feat(cookies): enhance CookieList API with detailed documentation and method improvements

- Updated the `CookieList` class to provide comprehensive documentation on cookie management methods, including `add`, `upsert`, `remove`, and `delete`.
- Improved method signatures to support both callback and Promise-based usage for asynchronous operations.
- Added detailed descriptions for read and write methods, including examples and expected behavior.
- Enhanced the integration of the `CookieList` with the QuickJS VM by updating the property list bridge to include `toJSON` in sync read object methods.

* feat(cookies): add detailed examples and improve async bridge documentation

- Enhanced the `createPropertyListBridge` function documentation with comprehensive examples for setting up cookie methods in QuickJS.
- Clarified the two-phase setup process for async write methods, detailing the registration of bridge functions and the generation of JavaScript code for method wrappers.
- Added a new test case for the `toJSON()` method to ensure it returns a cloned array of all cookies, validating the expected structure and properties.

* fix(assert-runtime): correct syntax error in response parser assignment

- Added a semicolon at the end of the response parser assignment to ensure proper syntax in the AssertRuntime class.
2026-03-27 19:42:23 +05:30

346 lines
12 KiB
JavaScript

const CookieList = require('../src/cookie-list');
const PropertyList = require('../src/property-list');
const ReadOnlyPropertyList = require('../src/readonly-property-list');
describe('CookieList', () => {
const mockCookies = [
{ key: 'session', value: 'abc123' },
{ key: 'token', value: 'xyz789' },
{ key: 'theme', value: 'dark' }
];
function createCookieList(overrides = {}) {
return new CookieList({
getUrl: overrides.getUrl || (() => 'https://example.com'),
interpolate: overrides.interpolate || ((str) => str),
createCookieJar: overrides.createCookieJar || (() => ({})),
getCookiesForUrl: overrides.getCookiesForUrl || (() => mockCookies)
});
}
// ── Inheritance ────────────────────────────────────────────────────────
test('extends PropertyList and ReadOnlyPropertyList', () => {
const list = createCookieList();
expect(list).toBeInstanceOf(ReadOnlyPropertyList);
expect(list).toBeInstanceOf(PropertyList);
expect(list).toBeInstanceOf(CookieList);
});
test('inherits read methods from ReadOnlyPropertyList', () => {
const list = createCookieList();
expect(list.get('session')).toBe('abc123');
expect(list.all()).toHaveLength(3);
expect(list.count()).toBe(3);
});
test('normalizes tough-cookie objects to plain objects', () => {
const toughCookies = [
{ key: 'a', value: '1', domain: 'example.com', path: '/', secure: true, httpOnly: false, expires: null, _internal: 'hidden', creation: new Date(), store: { circular: true } }
];
const list = createCookieList({
getCookiesForUrl: () => toughCookies
});
const items = list.all();
expect(items).toHaveLength(1);
expect(items[0]).toEqual({ key: 'a', value: '1', domain: 'example.com', path: '/', secure: true, httpOnly: false, expires: null });
expect(items[0]).not.toHaveProperty('_internal');
expect(items[0]).not.toHaveProperty('creation');
expect(items[0]).not.toHaveProperty('store');
});
test('toJSON() returns cloned array of normalized cookies', () => {
const list = createCookieList();
const json = list.toJSON();
expect(json).toHaveLength(3);
expect(json[0]).toEqual({ key: 'session', value: 'abc123' });
});
// ── Read methods with no URL ───────────────────────────────────────────
describe('read methods when URL is falsy', () => {
let list;
beforeEach(() => {
list = createCookieList({ getUrl: () => null });
});
test('all() returns empty array', () => {
expect(list.all()).toEqual([]);
});
test('count() returns 0', () => {
expect(list.count()).toBe(0);
});
test('get() returns undefined', () => {
expect(list.get('session')).toBeUndefined();
});
test('has() returns false', () => {
expect(list.has('session')).toBe(false);
});
});
// ── Write methods (cookie jar delegation) ──────────────────────────────
describe('add()', () => {
test('delegates to jar.setCookie', () => {
const setCookie = jest.fn().mockResolvedValue('ok');
const list = createCookieList({
createCookieJar: () => ({ setCookie })
});
list.add({ name: 'foo', value: 'bar' });
expect(setCookie).toHaveBeenCalledWith('https://example.com', { name: 'foo', value: 'bar' }, undefined);
});
test('passes callback to jar.setCookie', () => {
const setCookie = jest.fn();
const cb = jest.fn();
const list = createCookieList({
createCookieJar: () => ({ setCookie })
});
list.add({ name: 'foo', value: 'bar' }, cb);
expect(setCookie).toHaveBeenCalledWith('https://example.com', { name: 'foo', value: 'bar' }, cb);
});
test('returns Promise.resolve() when no URL', async () => {
const list = createCookieList({ getUrl: () => null });
const result = list.add({ name: 'foo', value: 'bar' });
await expect(result).resolves.toBeUndefined();
});
test('calls callback with undefined when no URL', () => {
const cb = jest.fn();
const list = createCookieList({ getUrl: () => '' });
list.add({ name: 'foo', value: 'bar' }, cb);
expect(cb).toHaveBeenCalledWith(undefined);
});
});
describe('upsert()', () => {
test('delegates to jar.setCookie', () => {
const setCookie = jest.fn().mockResolvedValue('ok');
const list = createCookieList({
createCookieJar: () => ({ setCookie })
});
list.upsert({ name: 'foo', value: 'bar' });
expect(setCookie).toHaveBeenCalledWith('https://example.com', { name: 'foo', value: 'bar' }, undefined);
});
test('returns Promise.resolve() when no URL', async () => {
const list = createCookieList({ getUrl: () => null });
const result = list.upsert({ name: 'foo', value: 'bar' });
await expect(result).resolves.toBeUndefined();
});
test('rejects with error when cookieObj is null', async () => {
const list = createCookieList();
await expect(list.upsert(null)).rejects.toThrow('cookieObj must be a non-null object');
});
test('rejects with error when cookieObj is a string', async () => {
const list = createCookieList();
await expect(list.upsert('not-an-object')).rejects.toThrow('cookieObj must be a non-null object');
});
test('calls callback with error when cookieObj is null', () => {
const cb = jest.fn();
const list = createCookieList();
list.upsert(null, cb);
expect(cb).toHaveBeenCalledWith(expect.any(Error));
expect(cb.mock.calls[0][0].message).toBe('cookieObj must be a non-null object');
});
});
describe('remove()', () => {
test('delegates to jar.deleteCookie', () => {
const deleteCookie = jest.fn().mockResolvedValue('ok');
const list = createCookieList({
createCookieJar: () => ({ deleteCookie })
});
list.remove('session');
expect(deleteCookie).toHaveBeenCalledWith('https://example.com', 'session', undefined);
});
test('passes callback', () => {
const deleteCookie = jest.fn();
const cb = jest.fn();
const list = createCookieList({
createCookieJar: () => ({ deleteCookie })
});
list.remove('session', cb);
expect(deleteCookie).toHaveBeenCalledWith('https://example.com', 'session', cb);
});
test('returns Promise.resolve() when no URL', async () => {
const list = createCookieList({ getUrl: () => null });
const result = list.remove('session');
await expect(result).resolves.toBeUndefined();
});
test('returns Promise.resolve() when no name', async () => {
const list = createCookieList();
const result = list.remove(null);
await expect(result).resolves.toBeUndefined();
});
test('calls callback when no name', () => {
const cb = jest.fn();
const list = createCookieList();
list.remove('', cb);
expect(cb).toHaveBeenCalledWith(undefined);
});
});
describe('clear()', () => {
test('delegates to jar.deleteCookies', () => {
const deleteCookies = jest.fn().mockResolvedValue('ok');
const list = createCookieList({
createCookieJar: () => ({ deleteCookies })
});
list.clear();
expect(deleteCookies).toHaveBeenCalledWith('https://example.com', undefined);
});
test('passes callback', () => {
const deleteCookies = jest.fn();
const cb = jest.fn();
const list = createCookieList({
createCookieJar: () => ({ deleteCookies })
});
list.clear(cb);
expect(deleteCookies).toHaveBeenCalledWith('https://example.com', cb);
});
test('returns Promise.resolve() when no URL', async () => {
const list = createCookieList({ getUrl: () => null });
const result = list.clear();
await expect(result).resolves.toBeUndefined();
});
});
describe('delete()', () => {
test('delegates to jar.deleteCookie', () => {
const deleteCookie = jest.fn().mockResolvedValue('ok');
const list = createCookieList({
createCookieJar: () => ({ deleteCookie })
});
list.delete('session');
expect(deleteCookie).toHaveBeenCalledWith('https://example.com', 'session', undefined);
});
test('returns Promise.resolve() when no URL', async () => {
const list = createCookieList({ getUrl: () => null });
const result = list.delete('session');
await expect(result).resolves.toBeUndefined();
});
test('returns Promise.resolve() when no name', async () => {
const list = createCookieList();
const result = list.delete(null);
await expect(result).resolves.toBeUndefined();
});
});
// ── jar() ──────────────────────────────────────────────────────────────
describe('jar()', () => {
let mockJar;
let list;
beforeEach(() => {
mockJar = {
getCookie: jest.fn().mockResolvedValue({ key: 'session', value: 'abc' }),
getCookies: jest.fn().mockResolvedValue([]),
setCookie: jest.fn().mockResolvedValue('ok'),
setCookies: jest.fn().mockResolvedValue('ok'),
clear: jest.fn().mockResolvedValue('ok'),
deleteCookies: jest.fn().mockResolvedValue('ok'),
deleteCookie: jest.fn().mockResolvedValue('ok'),
hasCookie: jest.fn().mockResolvedValue(true)
};
list = createCookieList({
interpolate: (str) => str.replace('{{host}}', 'example.com'),
createCookieJar: () => mockJar
});
});
test('returns an object with all jar methods', () => {
const jar = list.jar();
expect(jar).toHaveProperty('getCookie');
expect(jar).toHaveProperty('getCookies');
expect(jar).toHaveProperty('setCookie');
expect(jar).toHaveProperty('setCookies');
expect(jar).toHaveProperty('clear');
expect(jar).toHaveProperty('deleteCookies');
expect(jar).toHaveProperty('deleteCookie');
expect(jar).toHaveProperty('hasCookie');
});
test('getCookie interpolates URL and delegates', () => {
const jar = list.jar();
const cb = jest.fn();
jar.getCookie('https://{{host}}/path', 'session', cb);
expect(mockJar.getCookie).toHaveBeenCalledWith('https://example.com/path', 'session', cb);
});
test('getCookies interpolates URL and delegates', () => {
const jar = list.jar();
const cb = jest.fn();
jar.getCookies('https://{{host}}/path', cb);
expect(mockJar.getCookies).toHaveBeenCalledWith('https://example.com/path', cb);
});
test('setCookie interpolates URL and delegates', () => {
const jar = list.jar();
const cb = jest.fn();
jar.setCookie('https://{{host}}/path', 'name', 'value', cb);
expect(mockJar.setCookie).toHaveBeenCalledWith('https://example.com/path', 'name', 'value', cb);
});
test('setCookies interpolates URL and delegates', () => {
const jar = list.jar();
const cb = jest.fn();
jar.setCookies('https://{{host}}/path', [{ name: 'a' }], cb);
expect(mockJar.setCookies).toHaveBeenCalledWith('https://example.com/path', [{ name: 'a' }], cb);
});
test('clear delegates directly', () => {
const jar = list.jar();
const cb = jest.fn();
jar.clear(cb);
expect(mockJar.clear).toHaveBeenCalledWith(cb);
});
test('deleteCookies interpolates URL and delegates', () => {
const jar = list.jar();
const cb = jest.fn();
jar.deleteCookies('https://{{host}}/path', cb);
expect(mockJar.deleteCookies).toHaveBeenCalledWith('https://example.com/path', cb);
});
test('deleteCookie interpolates URL and delegates', () => {
const jar = list.jar();
const cb = jest.fn();
jar.deleteCookie('https://{{host}}/path', 'session', cb);
expect(mockJar.deleteCookie).toHaveBeenCalledWith('https://example.com/path', 'session', cb);
});
test('hasCookie interpolates URL and delegates', () => {
const jar = list.jar();
const cb = jest.fn();
jar.hasCookie('https://{{host}}/path', 'session', cb);
expect(mockJar.hasCookie).toHaveBeenCalledWith('https://example.com/path', 'session', cb);
});
});
});