feat: add translations for direct cookie access methods (#7070)

* feat: add translations for direct cookie access methods

- Implement translations for pm.cookies.has, pm.cookies.get, and pm.cookies.toObject to their corresponding bru.cookies methods.
- Enhance the postman-to-bruno translator to handle these new cookie access patterns.
- Add unit tests to verify the correct conversion of cookie access methods in various scenarios.

* refactor: simplify optional member expression handling in postman-to-bruno translator

- Streamlined the code for handling optional member expressions in the translation of cookie access methods.
- Updated unit test to verify the correct output format for pm.cookies.toObject() conversion.

* refactor: enhance handling of await expressions in cookie translations

- Updated the postman-to-bruno translator to wrap await expressions in parentheses for improved clarity and consistency.
- Adjusted unit tests to reflect the new output format for cookie access methods, ensuring accurate translation of pm.cookies.get calls.

* refactor: update cookie access translations to use hasCookie method

- Modified translations for pm.cookies.has to utilize the new bru.cookies.hasCookie method for improved clarity and functionality.
- Updated related unit tests to reflect changes in expected output for cookie existence checks.
- Added new tests to validate the behavior of the hasCookie method in various scenarios.
This commit is contained in:
sanish chirayath
2026-02-12 14:30:35 +05:30
committed by GitHub
parent 836c2b9ace
commit d30ab4d984
7 changed files with 237 additions and 1 deletions

View File

@@ -68,6 +68,10 @@ const replacements = {
'pm\\.execution\\.skipRequest': 'bru.runner.skipRequest',
'pm\\.execution\\.setNextRequest\\(null\\)': 'bru.runner.stopExecution()',
'pm\\.execution\\.setNextRequest\\(\'null\'\\)': 'bru.runner.stopExecution()',
// Direct cookie access translations (pm.cookies.has/get/toObject)
'pm\\.cookies\\.has\\(([^)]+)\\)': 'await bru.cookies.jar().hasCookie(req.getUrl(), $1)',
'pm\\.cookies\\.get\\(([^)]+)\\)': '(await bru.cookies.jar().getCookie(req.getUrl(), $1))?.value',
'pm\\.cookies\\.toObject\\(\\)': '(await bru.cookies.jar().getCookies(req.getUrl())).reduce((obj, c) => ({...obj, [c.key]: c.value}), {})',
// Cookie jar translations
'pm\\.cookies\\.jar\\(\\)': 'bru.cookies.jar()',
'pm\\.cookies\\.jar\\(\\)\\.get\\(': 'bru.cookies.jar().getCookie(',

View File

@@ -240,6 +240,94 @@ const complexTransformations = [
args
);
}
},
// pm.cookies.has(name) → await bru.cookies.jar().hasCookie(req.getUrl(), name)
{
pattern: 'pm.cookies.has',
transform: (path, j) => {
const callExpr = path.parent.value;
const args = callExpr.arguments;
const hasCookieCall = j.callExpression(
j.identifier('bru.cookies.jar().hasCookie'),
[j.identifier('req.getUrl()'), ...args]
);
return j.awaitExpression(hasCookieCall);
}
},
// pm.cookies.get(name) → (await bru.cookies.jar().getCookie(req.getUrl(), name))?.value
{
pattern: 'pm.cookies.get',
transform: (path, j) => {
const callExpr = path.parent.value;
const args = callExpr.arguments;
const getCookieCall = j.callExpression(
j.identifier('bru.cookies.jar().getCookie'),
[j.identifier('req.getUrl()'), ...args]
);
const awaitExpr = j.awaitExpression(getCookieCall);
const parenAwait = j.parenthesizedExpression
? j.parenthesizedExpression(awaitExpr)
: awaitExpr;
return j.optionalMemberExpression(
parenAwait,
j.identifier('value'),
false,
true
);
}
},
// pm.cookies.toObject() → (await bru.cookies.jar().getCookies(req.getUrl())).reduce((obj, c) => ({...obj, [c.key]: c.value}), {})
{
pattern: 'pm.cookies.toObject',
transform: (path, j) => {
const getCookiesCall = j.callExpression(
j.identifier('bru.cookies.jar().getCookies'),
[j.identifier('req.getUrl()')]
);
const awaitExpr = j.awaitExpression(getCookiesCall);
// Build the reduce callback: (obj, c) => ({...obj, [c.key]: c.value})
const objParam = j.identifier('obj');
const cParam = j.identifier('c');
const spreadElement = j.spreadElement(objParam);
const computedProp = j.property(
'init',
j.memberExpression(cParam, j.identifier('key')),
j.memberExpression(cParam, j.identifier('value'))
);
computedProp.computed = true;
const objectExpr = j.objectExpression([spreadElement, computedProp]);
const arrowBody = j.parenthesizedExpression
? j.parenthesizedExpression(objectExpr)
: objectExpr;
const reduceFn = j.arrowFunctionExpression(
[objParam, cParam],
arrowBody
);
reduceFn.expression = true;
// Build: (await ...).reduce(fn, {})
return j.callExpression(
j.memberExpression(
awaitExpr,
j.identifier('reduce')
),
[reduceFn, j.objectExpression([])]
);
}
}
];
@@ -249,7 +337,7 @@ complexTransformations.forEach((transform) => {
complexTransformationsMap[transform.pattern] = transform;
});
const varInitsToReplace = new Set(['pm', 'postman', 'pm.request', 'pm.response', 'pm.test', 'pm.expect', 'pm.environment', 'pm.variables', 'pm.collectionVariables', 'pm.execution', 'pm.globals']);
const varInitsToReplace = new Set(['pm', 'postman', 'pm.request', 'pm.response', 'pm.test', 'pm.expect', 'pm.environment', 'pm.variables', 'pm.collectionVariables', 'pm.execution', 'pm.globals', 'pm.cookies']);
/**
* Process all transformations (both simple and complex) in the AST in a single pass

View File

@@ -316,4 +316,57 @@ describe('postmanTranslations - cookie API conversions', () => {
expect(postmanTranslation(inputScript)).toBe(expectedOutput);
});
// Tests for pm.cookies direct access methods (has, get, toObject)
test('should convert pm.cookies.has(name) to await hasCookie', () => {
const inputScript = `pm.cookies.has('token')`;
const expectedOutput = `await bru.cookies.jar().hasCookie(req.getUrl(), 'token')`;
expect(postmanTranslation(inputScript)).toBe(expectedOutput);
});
test('should convert pm.cookies.get(name) to await getCookie?.value', () => {
const inputScript = `pm.cookies.get('token')`;
const expectedOutput = `(await bru.cookies.jar().getCookie(req.getUrl(), 'token'))?.value`;
expect(postmanTranslation(inputScript)).toBe(expectedOutput);
});
test('should convert pm.cookies.toObject() to getCookies reduce', () => {
const inputScript = `pm.cookies.toObject()`;
const expectedOutput = `(await bru.cookies.jar().getCookies(req.getUrl())).reduce((obj, c) => ({
...obj,
[c.key]: c.value
}), {})`;
expect(postmanTranslation(inputScript)).toBe(expectedOutput);
});
test('should convert pm.cookies.has inside an if conditional', () => {
const inputScript = `if (pm.cookies.has('auth')) { console.log('found'); }`;
const expectedOutput = `if (await bru.cookies.jar().hasCookie(req.getUrl(), 'auth')) { console.log('found'); }`;
expect(postmanTranslation(inputScript)).toBe(expectedOutput);
});
test('should convert pm.cookies.get with a variable argument', () => {
const inputScript = `const val = pm.cookies.get(cookieName)`;
const expectedOutput = `const val = (await bru.cookies.jar().getCookie(req.getUrl(), cookieName))?.value`;
expect(postmanTranslation(inputScript)).toBe(expectedOutput);
});
test('should handle mixed pm.cookies.get and pm.cookies.jar().set without conflict', () => {
const inputScript = `const v = pm.cookies.get('token'); pm.cookies.jar().set('https://example.com', 'a', 'b');`;
const expectedOutput = `const v = (await bru.cookies.jar().getCookie(req.getUrl(), 'token'))?.value; bru.cookies.jar().setCookie('https://example.com', 'a', 'b');`;
expect(postmanTranslation(inputScript)).toBe(expectedOutput);
});
test('should handle combined has + get in same script', () => {
const inputScript = `if (pm.cookies.has('auth')) { const token = pm.cookies.get('auth'); }`;
const expectedOutput = `if (await bru.cookies.jar().hasCookie(req.getUrl(), 'auth')) { const token = (await bru.cookies.jar().getCookie(req.getUrl(), 'auth'))?.value; }`;
expect(postmanTranslation(inputScript)).toBe(expectedOutput);
});
test('should handle aliased access: const cookies = pm.cookies', () => {
const inputScript = `const cookies = pm.cookies; cookies.get('token');`;
const expectedOutput = `(await bru.cookies.jar().getCookie(req.getUrl(), 'token'))?.value;`;
expect(postmanTranslation(inputScript)).toBe(expectedOutput);
});
});

View File

@@ -81,6 +81,11 @@ class Bru {
deleteCookie: (url, cookieName, callback) => {
const interpolatedUrl = this.interpolate(url);
return cookieJar.deleteCookie(interpolatedUrl, cookieName, callback);
},
hasCookie: (url, cookieName, callback) => {
const interpolatedUrl = this.interpolate(url);
return cookieJar.hasCookie(interpolatedUrl, cookieName, callback);
}
};
}

View File

@@ -142,6 +142,19 @@ describe('Bruno Cookie Jar Wrapper - API Examples', () => {
});
});
describe('hasCookie', () => {
test('hasCookie returns true for existing cookie', async () => {
await jar.setCookie(testUrl, 'authToken', 'jwt123');
const exists = await jar.hasCookie(testUrl, 'authToken');
expect(exists).toBe(true);
});
test('hasCookie returns false for non-existent cookie', async () => {
const exists = await jar.hasCookie(testUrl, 'nonexistent');
expect(exists).toBe(false);
});
});
describe('Error Handling', () => {
test('setCookie handles missing URL', async () => {
await expect(jar.setCookie('', 'name', 'value')).rejects.toThrow('URL is required');

View File

@@ -214,6 +214,35 @@ const cookieJarWrapper = () => {
});
},
// Check whether a cookie with the given name exists for the URL.
hasCookie: function (
url: string,
cookieName: string,
callback?: (err: Error | null | undefined, exists?: boolean) => void
) {
if (!url || !cookieName) {
const error = new Error('URL and cookie name are required');
if (callback) return callback(error);
return Promise.reject(error);
}
if (callback) {
return cookieJar.getCookies(url, (err: Error | null, cookies?: Cookie[]) => {
if (err) return callback(err);
const cookieList = cookies || [];
callback(null, cookieList.some((c) => c.key === cookieName));
});
}
return new Promise<boolean>((resolve, reject) => {
cookieJar.getCookies(url, (err: Error | null, cookies?: Cookie[]) => {
if (err) return reject(err);
const cookieList = cookies || [];
resolve(cookieList.some((c) => c.key === cookieName));
});
});
},
// Get all cookies that would be sent to the given URL.
getCookies: function (url: string, callback?: (err: Error | null | undefined, cookies?: Cookie[]) => void) {
if (!url) {

View File

@@ -0,0 +1,44 @@
meta {
name: hasCookie
type: http
seq: 10
}
get {
url: {{host}}/ping
body: none
auth: inherit
}
script:pre-request {
const jar = bru.cookies.jar()
jar.setCookie("https://testbench-sanity.usebruno.com", "existing_cookie", "some_value")
}
tests {
const jar = bru.cookies.jar()
test("should return true for a cookie that exists", async function() {
const exists = await jar.hasCookie('https://testbench-sanity.usebruno.com', 'existing_cookie');
expect(exists).to.be.true;
});
test("should return false for a cookie that does not exist", async function() {
const exists = await jar.hasCookie('https://testbench-sanity.usebruno.com', 'nonexistent_cookie');
expect(exists).to.be.false;
});
jar.hasCookie("https://testbench-sanity.usebruno.com", "existing_cookie", function(error, exists) {
test("should work with callback pattern", function() {
expect(error).to.be.null;
expect(exists).to.be.true;
});
});
jar.clear()
}
settings {
encodeUrl: true
}