Implement legacy Postman global API transformations (#5403)

This commit is contained in:
sanish chirayath
2025-08-29 21:01:06 +05:30
committed by GitHub
parent 6bcb850b6e
commit cb7f61ee4b
2 changed files with 399 additions and 0 deletions

View File

@@ -350,6 +350,9 @@ function translateCode(code) {
// Process all transformations in a single pass
processTransformations(ast, transformedNodes);
// Handle legacy Postman global APIs
handleLegacyGlobalAPIs(ast, transformedNodes, code);
// Handle special Postman syntax patterns
handleTestsBracketNotation(ast);
@@ -787,5 +790,102 @@ function handleTestsBracketNotation(ast) {
});
}
/**
* Handle legacy Postman global API transformations
* This function processes legacy Postman globals like responseBody, responseHeaders, responseTime
* while preserving user-defined variables with the same names
*
* @param {Object} ast - jscodeshift AST
* @param {Set} transformedNodes - Set of already transformed nodes
* @param {string} code - The original Postman script code
*/
function handleLegacyGlobalAPIs(ast, transformedNodes, code) {
// regex check before the ast traversal
const legacyGlobalRegex = /responseBody|responseHeaders|responseTime/;
if (!legacyGlobalRegex.test(code)) {
return;
}
// Check for variable declarations with legacy global names - track which ones have conflicts
const conflictingNames = new Set();
// Check variable declarations
ast.find(j.VariableDeclarator).forEach(path => {
if (path.value.id.type === 'Identifier') {
const varName = path.value.id.name;
if (legacyGlobalRegex.test(varName)) {
conflictingNames.add(varName);
}
}
});
// Handle JSON.parse(responseBody) → res.getBody()
// Only transform if responseBody doesn't have a user variable conflict
if (!conflictingNames.has('responseBody')) {
ast.find(j.CallExpression).forEach(path => {
if (transformedNodes.has(path.node)) return;
const callExpr = path.value;
if (callExpr.callee.type === 'MemberExpression' && callExpr.callee.object.name === 'JSON' && callExpr.callee.property.name === 'parse') {
const args = callExpr.arguments;
// Check if the argument is 'responseBody'
if (args.length > 0 && args[0].type === 'Identifier' && args[0].name === 'responseBody') {
// Replace JSON.parse(responseBody) with res.getBody()
j(path).replaceWith(j.identifier('res.getBody()'));
transformedNodes.add(path.node);
}
}
});
}
// Handle standalone legacy Postman global variables
const legacyGlobals = [
{ name: 'responseBody', replacement: 'res.getBody()' },
{ name: 'responseHeaders', replacement: 'res.getHeaders()' },
{ name: 'responseTime', replacement: 'res.getResponseTime()' }
];
legacyGlobals.forEach(({ name, replacement }) => {
// Skip transformation if this name has a user variable conflict
if (conflictingNames.has(name)) {
return;
}
ast.find(j.Identifier, { name }).forEach(path => {
if (transformedNodes.has(path.node)) return;
// Only transform identifiers that are being used as values, not as variable names
const parent = path.parent.value;
// Skip if this is part of a variable declaration (const responseBody = ...)
if (parent.type === 'VariableDeclarator' && parent.id === path.node) {
return; // Keep unchanged
}
// Skip if this is part of an assignment (responseBody = ...)
if (parent.type === 'AssignmentExpression' && parent.left === path.node) {
return; // Keep unchanged
}
// Skip if this is part of a function parameter
if (parent.type === 'FunctionDeclaration' || parent.type === 'FunctionExpression') {
return; // Keep unchanged
}
// Skip if this is part of an object property
if (parent.type === 'Property' && (parent.key === path.node || parent.value === path.node)) {
return; // Keep unchanged
}
// Transform all other references (including function call arguments)
// This will transform console.log(responseBody) → console.log(res.getBody())
j(path).replaceWith(j.identifier(replacement));
transformedNodes.add(path.node);
});
});
}
export { getMemberExpressionString };
export default translateCode;

View File

@@ -0,0 +1,299 @@
import translateCode from '../../../../src/utils/jscode-shift-translator.js';
describe('Legacy Postman API Translation', () => {
describe('handleLegacyGlobalAPIs - No Conflicts', () => {
test('should translate responseBody when no user variables exist', () => {
const input = `
const data = JSON.parse(responseBody);
`;
const result = translateCode(input);
const expected = `
const data = res.getBody();
`;
expect(result).toEqual(expected);
});
test('should translate responseHeaders when no user variables exist', () => {
const input = `
console.log(responseHeaders);
const headers = responseHeaders;
`;
const result = translateCode(input);
expect(result).toContain('res.getHeaders()');
expect(result).not.toContain('responseHeaders');
});
test('should translate responseTime when no user variables exist', () => {
const input = `
console.log(responseTime);
const time = responseTime;
`;
const result = translateCode(input);
expect(result).toContain('res.getResponseTime()');
expect(result).not.toContain('responseTime');
});
test('should translate JSON.parse(responseBody) when no user variables exist', () => {
const input = `
const data = JSON.parse(responseBody);
console.log(data);
`;
const result = translateCode(input);
expect(result).toContain('res.getBody()');
expect(result).not.toContain('JSON.parse(responseBody)');
expect(result).not.toContain('responseBody');
});
test('should translate JSON.parse(responseBody) usage without assignment when no user variables exist', () => {
const input = `
console.log(JSON.parse(responseBody));
`;
const result = translateCode(input);
const expected = `
console.log(res.getBody());
`;
expect(result).toContain(expected);
});
test('should translate all legacy APIs when no conflicts exist', () => {
const input = `
const data = JSON.parse(responseBody);
const headers = responseHeaders;
const time = responseTime;
console.log(data, headers, time);
`;
const result = translateCode(input);
expect(result).toContain('res.getBody()');
expect(result).toContain('res.getHeaders()');
expect(result).toContain('res.getResponseTime()');
expect(result).not.toContain('responseBody');
expect(result).not.toContain('responseHeaders');
expect(result).not.toContain('responseTime');
});
});
describe('handleLegacyGlobalAPIs - With Conflicts', () => {
test('should NOT translate responseBody when user variable exists', () => {
const input = `
const responseBody = pm.response.json();
console.log(responseBody);
`;
const result = translateCode(input);
const expected = `
const responseBody = res.getBody();
console.log(responseBody);
`;
// pm.response.json() should be transformed to res.getBody() (Postman API transformation)
expect(result).toEqual(expected);
});
test('should NOT translate responseHeaders when user variable exists', () => {
const input = `
const responseHeaders = pm.response.headers;
console.log(responseHeaders);
`;
const result = translateCode(input);
const expected = `
const responseHeaders = res.getHeaders();
console.log(responseHeaders);
`;
expect(result).toEqual(expected);
});
test('should NOT translate responseTime when user variable exists', () => {
const input = `
const responseTime = pm.response.responseTime;
console.log(responseTime);
`;
const result = translateCode(input);
const expected = `
const responseTime = res.getResponseTime();
console.log(responseTime);
`;
expect(result).toEqual(expected);
});
test('should NOT translate JSON.parse(responseBody) when user variable exists', () => {
const input = `
const responseBody = pm.response.json();
const data = JSON.parse(responseBody);
console.log(data);
`;
const result = translateCode(input);
const expected = `
const responseBody = res.getBody();
const data = JSON.parse(responseBody);
console.log(data);
`;
expect(result).toEqual(expected);
});
});
describe('handleLegacyGlobalAPIs - Partial Conflicts', () => {
test('should translate non-conflicting APIs when some conflicts exist', () => {
const input = `
const responseBody = pm.response.json();
console.log(responseBody);
console.log(responseHeaders);
console.log(responseTime);
`;
const result = translateCode(input);
const expected = `
const responseBody = res.getBody();
console.log(responseBody);
console.log(res.getHeaders());
console.log(res.getResponseTime());
`;
expect(result).toEqual(expected);
});
test('should translate JSON.parse(responseBody) only when no conflict exists', () => {
const input = `
const responseHeaders = pm.response.headers;
const data = JSON.parse(responseBody);
console.log(responseHeaders);
`;
const result = translateCode(input);
const expected = `
const responseHeaders = res.getHeaders();
const data = res.getBody();
console.log(responseHeaders);
`;
expect(result).toEqual(expected);
});
});
describe('handleLegacyGlobalAPIs - Edge Cases', () => {
test.skip('should handle function parameters with legacy names', () => {
const input = `
function test(responseBody) {
console.log(responseBody);
console.log(responseHeaders);
}
`;
const result = translateCode(input);
const expected = `
function test(responseBody) {
console.log(responseBody);
console.log(res.getHeaders());
}
`;
expect(result).toEqual(expected);
});
test('should handle object properties with legacy names', () => {
const input = `
const config = {
responseBody: 'custom',
responseHeaders: 'custom'
};
console.log(responseTime);
`;
const result = translateCode(input);
const expected = `
const config = {
responseBody: 'custom',
responseHeaders: 'custom'
};
console.log(res.getResponseTime());
`;
expect(result).toEqual(expected);
});
test('should handle assignments with legacy names', () => {
const input = `
responseBody = 'new value';
responseHeaders = 'new headers';
console.log(responseTime);
`;
const result = translateCode(input);
const expected = `
responseBody = 'new value';
responseHeaders = 'new headers';
console.log(res.getResponseTime());
`;
expect(result).toEqual(expected);
});
test('should handle mixed usage patterns', () => {
const input = `
const responseBody = pm.response.json();
const data = JSON.parse(responseBody);
console.log(responseHeaders);
console.log(responseTime);
function test(data) {
console.log(responseBody);
console.log(responseHeaders);
}
`;
const result = translateCode(input);
const expected = `
const responseBody = res.getBody();
const data = JSON.parse(responseBody);
console.log(res.getHeaders());
console.log(res.getResponseTime());
function test(data) {
console.log(responseBody);
console.log(res.getHeaders());
}
`;
expect(result).toEqual(expected);
});
});
describe('handleLegacyGlobalAPIs - No Legacy APIs', () => {
test('should not modify code when no legacy APIs are present', () => {
const input = `
const data = { name: 'test' };
console.log(data.name);
`;
const result = translateCode(input);
const expected = `
const data = { name: 'test' };
console.log(data.name);
`;
expect(result).toEqual(expected);
});
});
});