Fix/add missing translations (#4637)

* fix: add missing deps
* feat: add missing translations
* fix: regex tranasaltion for to.have.headers
This commit is contained in:
Anoop M D
2025-05-10 16:59:21 +05:30
committed by GitHub
7 changed files with 221 additions and 15 deletions

15
package-lock.json generated
View File

@@ -8290,7 +8290,6 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz",
"integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/lodash": {
@@ -8313,7 +8312,6 @@
"version": "12.2.3",
"resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz",
"integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/linkify-it": "*",
@@ -8324,7 +8322,6 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz",
"integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/ms": {
@@ -13266,7 +13263,6 @@
"version": "0.1.13",
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz",
"integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
@@ -14733,6 +14729,15 @@
"dev": true,
"license": "ISC"
},
"node_modules/flow-parser": {
"version": "0.269.1",
"resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.269.1.tgz",
"integrity": "sha512-2Yr0kqvT7RwaGL192nT78O5AWJeECQjl0NEzBkMsx8OJt63BvNl5yvSIbE4qZ1VDSjEkhbUgaWYdwX354bVNjw==",
"license": "MIT",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/follow-redirects": {
"version": "1.15.9",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
@@ -26469,7 +26474,7 @@
"version": "4.9.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
"dev": true,
"devOptional": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",

View File

@@ -20,6 +20,11 @@ const replacements = {
'pm\\.response\\.text\\(\\)': 'JSON.stringify(res.getBody())',
'pm\\.expect\\.fail\\(': 'expect.fail(',
'pm\\.response\\.responseTime': 'res.getResponseTime()',
'pm\\.globals\\.set\\(': 'bru.setGlobalEnvVar(',
'pm\\.globals\\.get\\(': 'bru.getGlobalEnvVar(',
'pm\\.response\\.headers\\.get\\(': 'res.getHeader(',
'pm\\.response\\.to\\.have\\.body\\(': 'expect(res.getBody()).to.equal(',
'pm\\.response\\.to\\.have\\.header\\(': 'expect(res.getHeaders()).to.have.property(',
'pm\\.environment\\.name': 'bru.getEnvName()',
'pm\\.response\\.status': 'res.statusText',
'pm\\.response\\.headers': 'res.getHeaders()',

View File

@@ -38,6 +38,10 @@ function getMemberExpressionString(node) {
// Simple 1:1 translations for straightforward replacements
const simpleTranslations = {
// Global Variables
'pm.globals.get': 'bru.getGlobalEnvVar',
'pm.globals.set': 'bru.setGlobalEnvVar',
// Environment variables
'pm.environment.get': 'bru.getEnvVar',
'pm.environment.set': 'bru.setEnvVar',
@@ -124,7 +128,12 @@ const complexTransformations = [
return j.callExpression(j.identifier('JSON.stringify'), [j.identifier('res.getBody()')]);
}
},
{
pattern: 'pm.response.headers.get',
transform: (path, j) => {
return j.callExpression(j.identifier('res.getHeader'), path.parent.value.arguments);
}
},
// Handle pm.response.to.have.status
{
pattern: 'pm.response.to.have.status',
@@ -191,6 +200,24 @@ const complexTransformations = [
}
},
// handle pm.response.to.have.body to expect(res.getBody()).to.equal(arg)
{
pattern: 'pm.response.to.have.body',
transform: (path, j) => {
const callExpr = path.parent.value;
const args = callExpr.arguments;
return j.callExpression(
j.memberExpression(
j.callExpression(j.identifier('expect'), [j.identifier('res.getBody()')]),
j.identifier('to.equal')
),
args
);
}
},
// Handle pm.execution.setNextRequest(null)
{
@@ -225,7 +252,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']);
const varInitsToReplace = new Set(['pm', 'postman', 'pm.request','pm.response', 'pm.test', 'pm.expect', 'pm.environment', 'pm.variables', 'pm.collectionVariables', 'pm.execution', 'pm.globals']);
/**
* Process all transformations (both simple and complex) in the AST in a single pass

View File

@@ -411,8 +411,85 @@ describe('Combined API Features Translation', () => {
`;
const translatedCode = translateCode(code);
expect(translatedCode).toBe(`
const globals = pm.globals;
const key = globals.get("key");
const key = bru.getGlobalEnvVar("key");
`);
})
it('should handle pm.response.to.have.body integrated with other assertions', () => {
const code = `
pm.test("Response validation", function() {
pm.response.to.have.status(200);
pm.response.to.have.body({"success": true});
pm.response.to.have.header("Content-Type", "application/json");
});
`;
const translatedCode = translateCode(code);
const expectedOutput = `
test("Response validation", function() {
expect(res.getStatus()).to.equal(200);
expect(res.getBody()).to.equal({"success": true});
expect(res.getHeaders()).to.have.property("Content-Type".toLowerCase(), "application/json");
});
`;
expect(translatedCode).toBe(expectedOutput);
});
it('should handle pm.response.to.have.body with dynamic content', () => {
const code = `
const expectedResponse = {
id: pm.environment.get("userId"),
token: pm.variables.get("authToken"),
timestamp: new Date().getTime()
};
pm.test("Dynamic response validation", function() {
pm.response.to.have.body(expectedResponse);
});
`;
const translatedCode = translateCode(code);
const expectedOutput = `
const expectedResponse = {
id: bru.getEnvVar("userId"),
token: bru.getVar("authToken"),
timestamp: new Date().getTime()
};
test("Dynamic response validation", function() {
expect(res.getBody()).to.equal(expectedResponse);
});
`
expect(translatedCode).toBe(expectedOutput);
});
it('should handle pm.response.to.have.body in control structures', () => {
const code = `
const jsonData = pm.response.json();
if (jsonData.status === "success") {
pm.response.to.have.body({
status: "success",
data: jsonData.data
});
} else {
pm.expect(jsonData.error).to.exist;
}
`;
const translatedCode = translateCode(code);
const expectedOutput = `
const jsonData = res.getBody();
if (jsonData.status === "success") {
expect(res.getBody()).to.equal({
status: "success",
data: jsonData.data
});
} else {
expect(jsonData.error).to.exist;
}
`;
expect(translatedCode).toBe(expectedOutput);
});
});

View File

@@ -27,7 +27,7 @@ describe('Legacy Tests[] Syntax Translation', () => {
const translatedCode = translateCode(code);
expect(translatedCode).toBe(`
test("Content-Type is application/json", function() {
expect(Boolean(res.getHeaders().get("Content-Type") === "application/json")).to.be.true;
expect(Boolean(res.getHeader("Content-Type") === "application/json")).to.be.true;
});`);
});
@@ -273,7 +273,7 @@ describe('Legacy Tests[] Syntax Translation', () => {
expect(translatedCode).toContain('test("Has content-type header", function() {');
expect(translatedCode).toContain('expect(Boolean(res.getHeaders().has("Content-Type"))).to.be.true;');
expect(translatedCode).toContain('test("Content-Type is JSON", function() {');
expect(translatedCode).toContain('expect(Boolean(res.getHeaders().get("Content-Type").includes("application/json"))).to.be.true;');
expect(translatedCode).toContain('expect(Boolean(res.getHeader("Content-Type").includes("application/json"))).to.be.true;');
expect(translatedCode).toContain('const expectedItems = parseInt(bru.getEnvVar("expectedItemCount"));');
expect(translatedCode).toContain('test("Has correct number of items", function() {');
expect(translatedCode).toContain('expect(Boolean(response.items.length === expectedItems)).to.be.true;');

View File

@@ -32,6 +32,12 @@ describe('Response Translation', () => {
expect(translatedCode).toBe('console.log("Status text:", res.statusText);');
});
it('should translate pm.response.headers', () => {
const code = 'console.log("Headers:", pm.response.headers);';
const translatedCode = translateCode(code);
expect(translatedCode).toBe('console.log("Headers:", res.getHeaders());');
});
// Complex response transformations
it('should transform pm.response.to.have.status', () => {
const code = 'pm.response.to.have.status(201);';
@@ -178,6 +184,17 @@ describe('Response Translation', () => {
`);
});
it('should translate response.headers', () => {
const code = `
const resp = pm.response;
const headers = resp.headers;
`;
const translatedCode = translateCode(code);
expect(translatedCode).toBe(`
const headers = res.getHeaders();
`);
});
it('should translate pm.response.statusText', () => {
const code = `
const resp = pm.response;
@@ -296,8 +313,8 @@ describe('Response Translation', () => {
const translatedCode = translateCode(code);
// Check how header access is translated
expect(translatedCode).toContain('const contentType = res.getHeaders().get(\'Content-Type\');');
expect(translatedCode).toContain('const contentLength = res.getHeaders().get(\'Content-Length\');');
expect(translatedCode).toContain('const contentType = res.getHeader(\'Content-Type\');');
expect(translatedCode).toContain('const contentLength = res.getHeader(\'Content-Length\');');
expect(translatedCode).toContain('console.log("contentType", contentType);');
expect(translatedCode).toContain('console.log("contentLength", contentLength);');
expect(translatedCode).not.toContain('pm.test')
@@ -340,7 +357,7 @@ describe('Response Translation', () => {
const translatedCode = translateCode(code);
expect(translatedCode).toContain('if (res.getStatus() >= 200 && res.getStatus() < 300) {');
expect(translatedCode).toContain('if (res.getHeaders().get(\'Content-Type\').includes(\'application/json\')) {');
expect(translatedCode).toContain('if (res.getHeader(\'Content-Type\').includes(\'application/json\')) {');
expect(translatedCode).toContain('const data = res.getBody();');
expect(translatedCode).toContain('bru.setEnvVar("authToken", data.token);');
expect(translatedCode).toContain('} else if (res.getStatus() === 404) {');
@@ -486,4 +503,54 @@ describe('Response Translation', () => {
expect(translatedCode).toContain('checkHeaderPresent("Authorization");');
expect(translatedCode).toContain('validateHeader("Content-Type", "application/json");');
});
it('should transform pm.response.to.have.body with string literal', () => {
const code = 'pm.response.to.have.body("Expected response body");';
const translatedCode = translateCode(code);
expect(translatedCode).toBe('expect(res.getBody()).to.equal("Expected response body");');
});
it('should transform pm.response.to.have.body with variable parameter', () => {
const code = `
const expectedBody = {"status": "success", "data": [1, 2, 3]};
pm.response.to.have.body(expectedBody);
`;
const translatedCode = translateCode(code);
expect(translatedCode).toContain('const expectedBody = {"status": "success", "data": [1, 2, 3]};');
expect(translatedCode).toContain('expect(res.getBody()).to.equal(expectedBody);');
});
it('should transform pm.response.to.have.body with JSON object', () => {
const code = `pm.response.to.have.body({"status": "success", "message": "Operation completed"});`;
const translatedCode = translateCode(code);
expect(translatedCode).toBe('expect(res.getBody()).to.equal({"status": "success", "message": "Operation completed"});');
});
it('should transform pm.response.to.have.body inside test function', () => {
const code = `
pm.test("Response body validation", function() {
const expectedResponse = {"result": true};
pm.response.to.have.body(expectedResponse);
});
`;
const translatedCode = translateCode(code);
const expectedOutput = `
test("Response body validation", function() {
const expectedResponse = {"result": true};
expect(res.getBody()).to.equal(expectedResponse);
});
`
expect(translatedCode).toBe(expectedOutput);
});
it('should transform pm.response.to.have.body with response alias', () => {
const code = `
const resp = pm.response;
resp.to.have.body({"status": "ok"});
`;
const translatedCode = translateCode(code);
expect(translatedCode).toBe(`
expect(res.getBody()).to.equal({"status": "ok"});
`);
});
});

View File

@@ -45,6 +45,18 @@ describe('Variables Translation', () => {
expect(translatedCode).toBe('bru.deleteVar("tempVar");');
});
it('should handle pm.globals.get', () => {
const code = 'pm.globals.get("test");';
const translatedCode = translateCode(code);
expect(translatedCode).toBe('bru.getGlobalEnvVar("test");');
});
it('should handle pm.globals.set', () => {
const code = 'pm.globals.set("test", "value");';
const translatedCode = translateCode(code);
expect(translatedCode).toBe('bru.setGlobalEnvVar("test", "value");');
});
// Alias tests for variables
it('should handle variables aliases', () => {
const code = `
@@ -79,6 +91,19 @@ describe('Variables Translation', () => {
`);
});
it('should handle pm.globals aliases', () => {
const code = `
const globals = pm.globals;
const get = globals.get("test");
const set = globals.set("test", "value");
`;
const translatedCode = translateCode(code);
expect(translatedCode).toBe(`
const get = bru.getGlobalEnvVar("test");
const set = bru.setGlobalEnvVar("test", "value");
`);
})
// Combined tests
it('should handle conditional expressions with variable calls', () => {
const code = 'const userStatus = pm.variables.has("userId") ? "logged-in" : "guest";';
@@ -124,5 +149,5 @@ describe('Variables Translation', () => {
const code = 'pm.collectionVariables.set("fullPath", pm.environment.get("baseUrl") + pm.variables.get("endpoint"));';
const translatedCode = translateCode(code);
expect(translatedCode).toBe('bru.setVar("fullPath", bru.getEnvVar("baseUrl") + bru.getVar("endpoint"));');
});
});
});