diff --git a/packages/bruno-app/src/utils/codemirror/autocomplete.js b/packages/bruno-app/src/utils/codemirror/autocomplete.js index 2e79fdaa7ef..820cb499de4 100644 --- a/packages/bruno-app/src/utils/codemirror/autocomplete.js +++ b/packages/bruno-app/src/utils/codemirror/autocomplete.js @@ -146,6 +146,27 @@ const STATIC_API_HINTS = { 'bru.runner.skipRequest()', 'bru.runner.stopExecution()', 'bru.interpolate(str)', + 'bru.variables', + 'bru.variables.get(key)', + 'bru.variables.set(key, value)', + 'bru.variables.has(key)', + 'bru.variables.unset(key)', + 'bru.variables.clear()', + 'bru.variables.toObject()', + 'bru.environment', + 'bru.environment.get(key)', + 'bru.environment.set(key, value)', + 'bru.environment.has(key)', + 'bru.environment.unset(key)', + 'bru.environment.clear()', + 'bru.environment.toObject()', + 'bru.environment.name', + 'bru.globals', + 'bru.globals.get(key)', + 'bru.globals.set(key, value)', + 'bru.globals.has(key)', + 'bru.globals.toObject()', + 'bru.globals.name', 'bru.cookies', 'bru.cookies.get(name)', 'bru.cookies.has(name)', diff --git a/packages/bruno-app/src/utils/collections/index.js b/packages/bruno-app/src/utils/collections/index.js index 0cc1b508e38..36921c5dd59 100644 --- a/packages/bruno-app/src/utils/collections/index.js +++ b/packages/bruno-app/src/utils/collections/index.js @@ -1146,6 +1146,7 @@ export const getGlobalEnvironmentVariables = ({ globalEnvironments, activeGlobal } }); } + variables.__name__ = environment?.name; return variables; }; diff --git a/packages/bruno-converters/src/postman/postman-translations.js b/packages/bruno-converters/src/postman/postman-translations.js index 9c60f07e227..e73bfac5af8 100644 --- a/packages/bruno-converters/src/postman/postman-translations.js +++ b/packages/bruno-converters/src/postman/postman-translations.js @@ -4,11 +4,35 @@ import translateCode from '../utils/postman-to-bruno-translator'; // Currently these APIs only work within the request lifecycle but fail to update the UI tables. // e.g., setCollectionVar only sets the variable in the request lifecycle, fails to update the table in the UI. const replacements = { - 'pm\\.environment\\.get\\(': 'bru.getEnvVar(', - 'pm\\.environment\\.set\\(': 'bru.setEnvVar(', - 'pm\\.variables\\.get\\(': 'bru.getVar(', - 'pm\\.variables\\.set\\(': 'bru.setVar(', + // Environment variables + 'pm\\.environment\\.get\\(': 'bru.environment.get(', + 'pm\\.environment\\.set\\(': 'bru.environment.set(', + 'pm\\.environment\\.has\\(': 'bru.environment.has(', + 'pm\\.environment\\.unset\\(': 'bru.environment.unset(', + 'pm\\.environment\\.replaceIn\\(': 'bru.interpolate(', + 'pm\\.environment\\.toObject\\(': 'bru.environment.toObject(', + 'pm\\.environment\\.clear\\(': 'bru.environment.clear(', + 'pm\\.environment\\.name': 'bru.environment.name', + + // Runtime variables + 'pm\\.variables\\.get\\(': 'bru.variables.get(', + 'pm\\.variables\\.set\\(': 'bru.variables.set(', + 'pm\\.variables\\.has\\(': 'bru.variables.has(', + 'pm\\.variables\\.unset\\(': 'bru.variables.unset(', 'pm\\.variables\\.replaceIn\\(': 'bru.interpolate(', + 'pm\\.variables\\.toObject\\(': 'bru.variables.toObject(', + 'pm\\.variables\\.clear\\(': 'bru.variables.clear(', + + // Global variables + 'pm\\.globals\\.get\\(': 'bru.globals.get(', + 'pm\\.globals\\.set\\(': 'bru.globals.set(', + 'pm\\.globals\\.has\\(': 'bru.globals.has(', + // 'pm\\.globals\\.unset\\(': 'bru.globals.unset(', // TODO: Re-enable once UI sync issue is resolved + 'pm\\.globals\\.replaceIn\\(': 'bru.interpolate(', + 'pm\\.globals\\.toObject\\(': 'bru.globals.toObject(', + // 'pm\\.globals\\.clear\\(': 'bru.globals.clear(', // TODO: Re-enable once UI sync issue is resolved + + // Collection variables 'pm\\.collectionVariables\\.get\\(': 'bru.getCollectionVar(', // 'pm\\.collectionVariables\\.set\\(': 'bru.setCollectionVar(', 'pm\\.collectionVariables\\.has\\(': 'bru.hasCollectionVar(', @@ -21,7 +45,6 @@ const replacements = { 'pm\\.response\\.to\\.have\\.status\\(': 'expect(res.getStatus()).to.equal(', 'pm\\.response\\.json\\(': 'res.getBody(', 'pm\\.expect\\(': 'expect(', - 'pm\\.environment\\.has\\(([^)]+)\\)': 'bru.getEnvVar($1) !== undefined && bru.getEnvVar($1) !== null', 'pm\\.response\\.code': 'res.getStatus()', 'pm\\.response\\.text\\(\\)': 'JSON.stringify(res.getBody())', 'pm\\.expect\\.fail\\(': 'expect.fail(', @@ -91,7 +114,6 @@ const replacements = { 'pm\\.response\\.responseSize': 'res.getSize().body', 'pm\\.response\\.size\\(\\)\\.header': 'res.getSize().header', 'pm\\.response\\.size\\(\\)\\.total': 'res.getSize().total', - 'pm\\.environment\\.name': 'bru.getEnvName()', 'pm\\.response\\.status': 'res.statusText', 'pm\\.response\\.headers': 'res.getHeaders()', 'tests\\[\'([^\']+)\'\\]\\s*=\\s*([^;]+);': 'test("$1", function() { expect(Boolean($2)).to.be.true; });', @@ -121,9 +143,9 @@ const replacements = { 'request\\.body': 'req.getBody()', 'request\\.name': 'req.getName()', // deprecated translations - 'postman\\.setEnvironmentVariable\\(': 'bru.setEnvVar(', - 'postman\\.getEnvironmentVariable\\(': 'bru.getEnvVar(', - 'postman\\.clearEnvironmentVariable\\(': 'bru.deleteEnvVar(', + 'postman\\.setEnvironmentVariable\\(': 'bru.environment.set(', + 'postman\\.getEnvironmentVariable\\(': 'bru.environment.get(', + 'postman\\.clearEnvironmentVariable\\(': 'bru.environment.unset(', 'pm\\.execution\\.skipRequest\\(\\)': 'bru.runner.skipRequest()', 'pm\\.execution\\.skipRequest': 'bru.runner.skipRequest', 'pm\\.execution\\.setNextRequest\\(null\\)': 'bru.runner.stopExecution()', diff --git a/packages/bruno-converters/src/utils/postman-to-bruno-translator.js b/packages/bruno-converters/src/utils/postman-to-bruno-translator.js index e99782997cc..cf65f650725 100644 --- a/packages/bruno-converters/src/utils/postman-to-bruno-translator.js +++ b/packages/bruno-converters/src/utils/postman-to-bruno-translator.js @@ -9,28 +9,32 @@ const cloneDeep = require('lodash/cloneDeep'); // e.g., setCollectionVar only sets the variable in the request lifecycle, fails to update the table in the UI. const simpleTranslations = { // Global Variables - 'pm.globals.get': 'bru.getGlobalEnvVar', - 'pm.globals.set': 'bru.setGlobalEnvVar', + 'pm.globals.get': 'bru.globals.get', + 'pm.globals.set': 'bru.globals.set', + 'pm.globals.has': 'bru.globals.has', + // 'pm.globals.unset': 'bru.globals.unset', // TODO: Re-enable once UI sync issue is resolved 'pm.globals.replaceIn': 'bru.interpolate', - // 'pm.globals.unset': 'bru.deleteGlobalEnvVar', - 'pm.globals.toObject': 'bru.getAllGlobalEnvVars', - // 'pm.globals.clear': 'bru.deleteAllGlobalEnvVars', + 'pm.globals.toObject': 'bru.globals.toObject', + // 'pm.globals.clear': 'bru.globals.clear', // TODO: Re-enable once UI sync issue is resolved // Environment variables - 'pm.environment.get': 'bru.getEnvVar', - 'pm.environment.set': 'bru.setEnvVar', - 'pm.environment.name': 'bru.getEnvName()', - 'pm.environment.unset': 'bru.deleteEnvVar', + 'pm.environment.get': 'bru.environment.get', + 'pm.environment.set': 'bru.environment.set', + 'pm.environment.has': 'bru.environment.has', + 'pm.environment.name': 'bru.environment.name', + 'pm.environment.unset': 'bru.environment.unset', 'pm.environment.replaceIn': 'bru.interpolate', - 'pm.environment.toObject': 'bru.getAllEnvVars', - 'pm.environment.clear': 'bru.deleteAllEnvVars', + 'pm.environment.toObject': 'bru.environment.toObject', + 'pm.environment.clear': 'bru.environment.clear', // Variables - 'pm.variables.get': 'bru.getVar', - 'pm.variables.set': 'bru.setVar', - 'pm.variables.has': 'bru.hasVar', - 'pm.variables.toObject': 'bru.getAllVars', + 'pm.variables.get': 'bru.variables.get', + 'pm.variables.set': 'bru.variables.set', + 'pm.variables.has': 'bru.variables.has', + 'pm.variables.unset': 'bru.variables.unset', + 'pm.variables.toObject': 'bru.variables.toObject', 'pm.variables.replaceIn': 'bru.interpolate', + 'pm.variables.clear': 'bru.variables.clear', // Collection variables 'pm.collectionVariables.get': 'bru.getCollectionVar', // 'pm.collectionVariables.set': 'bru.setCollectionVar', @@ -151,9 +155,9 @@ const simpleTranslations = { 'pm.execution.skipRequest': 'bru.runner.skipRequest', // Legacy Postman API (deprecated) (we can use pm instead of postman, as we are converting all postman references to pm in the code as the part of pre-processing) - 'pm.setEnvironmentVariable': 'bru.setEnvVar', - 'pm.getEnvironmentVariable': 'bru.getEnvVar', - 'pm.clearEnvironmentVariable': 'bru.deleteEnvVar', + 'pm.setEnvironmentVariable': 'bru.environment.set', + 'pm.getEnvironmentVariable': 'bru.environment.get', + 'pm.clearEnvironmentVariable': 'bru.environment.unset', // Legacy response properties 'responseCode.code': 'res.getStatus()', @@ -174,31 +178,6 @@ const complexTransformations = [ transform: sendRequestTransformer }, - // pm.environment.has requires special handling - { - pattern: 'pm.environment.has', - transform: (path, j) => { - const callExpr = path.parent.value; - - const args = callExpr.arguments; - - // Create: bru.getEnvVar(arg) !== undefined && bru.getEnvVar(arg) !== null - return j.logicalExpression( - '&&', - j.binaryExpression( - '!==', - j.callExpression(j.identifier('bru.getEnvVar'), args), - j.identifier('undefined') - ), - j.binaryExpression( - '!==', - j.callExpression(j.identifier('bru.getEnvVar'), args), - j.identifier('null') - ) - ); - } - }, - { pattern: 'pm.response.text', transform: (_, j) => { @@ -319,30 +298,6 @@ const complexTransformations = [ } }, - // pm.globals.has requires special handling - { - pattern: 'pm.globals.has', - transform: (path, j) => { - const callExpr = path.parent.value; - const args = callExpr.arguments; - - // Create: bru.getGlobalEnvVar(arg) !== undefined && bru.getGlobalEnvVar(arg) !== null - return j.logicalExpression( - '&&', - j.binaryExpression( - '!==', - j.callExpression(j.identifier('bru.getGlobalEnvVar'), args), - j.identifier('undefined') - ), - j.binaryExpression( - '!==', - j.callExpression(j.identifier('bru.getGlobalEnvVar'), args), - j.identifier('null') - ) - ); - } - }, - // pm.request.headers.add({key, value}) -> req.setHeader(key, value) { pattern: 'pm.request.headers.add', diff --git a/packages/bruno-converters/tests/postman/postman-translations/postman-comments.spec.js b/packages/bruno-converters/tests/postman/postman-translations/postman-comments.spec.js index e65075c27ce..5ff70102103 100644 --- a/packages/bruno-converters/tests/postman/postman-translations/postman-comments.spec.js +++ b/packages/bruno-converters/tests/postman/postman-translations/postman-comments.spec.js @@ -9,7 +9,7 @@ describe('postmanTranslations - comment handling', () => { `; const result = postmanTranslation(inputScript); expect(result).toContain('console.log(\'This script does not contain pm commands.\');'); - expect(result).toContain('const data = bru.getEnvVar(\'key\');'); + expect(result).toContain('const data = bru.environment.get(\'key\');'); }); // TODO: Restore once UI update fixes are live for setCollectionVar @@ -27,7 +27,7 @@ describe('postmanTranslations - comment handling', () => { test('should handle multiple pm commands on the same line', () => { const inputScript = 'pm.environment.get(\'key\'); pm.environment.set(\'key\', \'value\');'; - const expectedOutput = 'bru.getEnvVar(\'key\'); bru.setEnvVar(\'key\', \'value\');'; + const expectedOutput = 'bru.environment.get(\'key\'); bru.environment.set(\'key\', \'value\');'; expect(postmanTranslation(inputScript)).toBe(expectedOutput); }); @@ -45,11 +45,11 @@ describe('postmanTranslations - comment handling', () => { const expectedOutput = ` // This is a comment const value = 'test'; - bru.setEnvVar('key', value); + bru.environment.set('key', value); /* Multi-line comment */ - const result = bru.getEnvVar('key'); + const result = bru.environment.get('key'); console.log('Result:', result); `; expect(postmanTranslation(inputScript)).toBe(expectedOutput); diff --git a/packages/bruno-converters/tests/postman/postman-translations/postman-edge-cases.spec.js b/packages/bruno-converters/tests/postman/postman-translations/postman-edge-cases.spec.js index 22c567c5d85..83224d4c63a 100644 --- a/packages/bruno-converters/tests/postman/postman-translations/postman-edge-cases.spec.js +++ b/packages/bruno-converters/tests/postman/postman-translations/postman-edge-cases.spec.js @@ -25,8 +25,8 @@ describe('postmanTranslations - edge cases', () => { const expectedOutput = ` const sampleObjects = [ { - key: bru.getEnvVar('key'), - value: bru.getVar('value') + key: bru.environment.get('key'), + value: bru.variables.get('value') }, { key: bru.getCollectionVar('key'), @@ -35,11 +35,11 @@ describe('postmanTranslations - edge cases', () => { ]; const dataTesting = Object.entries(sampleObjects || {}).reduce((acc, [key, value]) => { // this is a comment - acc[key] = bru.getCollectionVar(bru.getEnvVar(value)); + acc[key] = bru.getCollectionVar(bru.environment.get(value)); return acc; // Return the accumulator }, {}); Object.values(dataTesting).forEach((data) => { - bru.setEnvVar(data.key, bru.getVar(data.value)); + bru.environment.set(data.key, bru.variables.get(data.value)); }); `; expect(postmanTranslation(inputScript)).toBe(expectedOutput); diff --git a/packages/bruno-converters/tests/postman/postman-translations/postman-variables.spec.js b/packages/bruno-converters/tests/postman/postman-translations/postman-variables.spec.js index a178d422dee..c93463e7714 100644 --- a/packages/bruno-converters/tests/postman/postman-translations/postman-variables.spec.js +++ b/packages/bruno-converters/tests/postman/postman-translations/postman-variables.spec.js @@ -7,8 +7,8 @@ describe('postmanTranslations - variables commands', () => { pm.environment.set('key', 'value'); `; const result = postmanTranslation(inputScript); - expect(result).toContain('bru.getEnvVar(\'key\')'); - expect(result).toContain('bru.setEnvVar(\'key\', \'value\')'); + expect(result).toContain('bru.environment.get(\'key\')'); + expect(result).toContain('bru.environment.set(\'key\', \'value\')'); }); test('should translate runtime variable commands', () => { @@ -17,8 +17,8 @@ describe('postmanTranslations - variables commands', () => { pm.variables.set('key', 'value'); `; const result = postmanTranslation(inputScript); - expect(result).toContain('bru.getVar(\'key\')'); - expect(result).toContain('bru.setVar(\'key\', \'value\')'); + expect(result).toContain('bru.variables.get(\'key\')'); + expect(result).toContain('bru.variables.set(\'key\', \'value\')'); }); test('should translate pm.collectionVariables.get', () => { @@ -30,7 +30,7 @@ describe('postmanTranslations - variables commands', () => { test('should translate pm.expect with pm.environment.has', () => { const inputScript = 'pm.expect(pm.environment.has(\'key\')).to.be.true;'; const result = postmanTranslation(inputScript); - expect(result).toContain('bru.getEnvVar(\'key\') !== undefined && bru.getEnvVar(\'key\') !== null'); + expect(result).toContain('bru.environment.has(\'key\')'); expect(result).toContain('.to.be.true'); }); diff --git a/packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/combined.test.js b/packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/combined.test.js index 82cafe46fdf..21586b0c0cb 100644 --- a/packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/combined.test.js +++ b/packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/combined.test.js @@ -93,11 +93,11 @@ describe('Combined API Features Translation', () => { expect(translatedCode).not.toContain('pm.test("Auth flow works", function() {'); expect(translatedCode).not.toContain('pm.expect(response.authenticated).to.be.true;'); expect(translatedCode).not.toContain('pm.environment.set("userId", response.user.id);'); - expect(translatedCode).toContain('const token = bru.getEnvVar("authToken");'); + expect(translatedCode).toContain('const token = bru.environment.get("authToken");'); expect(translatedCode).toContain('test("Auth flow works", function() {'); expect(translatedCode).toContain('const response = res.getBody();'); expect(translatedCode).toContain('expect(response.authenticated).to.be.true;'); - expect(translatedCode).toContain('bru.setEnvVar("userId", response.user.id);'); + expect(translatedCode).toContain('bru.environment.set("userId", response.user.id);'); }); // TODO: Restore once UI update fixes are live for setCollectionVar @@ -111,14 +111,14 @@ describe('Combined API Features Translation', () => { it('should handle nested Postman API calls', () => { const code = 'pm.environment.set("computed", pm.variables.get("base") + "-suffix");'; const translatedCode = translateCode(code); - expect(translatedCode).toBe('bru.setEnvVar("computed", bru.getVar("base") + "-suffix");'); + expect(translatedCode).toBe('bru.environment.set("computed", bru.variables.get("base") + "-suffix");'); }); // TODO: Restore once UI update fixes are live for setCollectionVar it.skip('should handle more complex nested expressions', () => { const code = 'pm.collectionVariables.set("fullPath", pm.environment.get("baseUrl") + pm.variables.get("endpoint"));'; const translatedCode = translateCode(code); - expect(translatedCode).toBe('bru.setCollectionVar("fullPath", bru.getEnvVar("baseUrl") + bru.getVar("endpoint"));'); + expect(translatedCode).toBe('bru.setCollectionVar("fullPath", bru.environment.get("baseUrl") + bru.variables.get("endpoint"));'); }); // Unrelated code @@ -141,7 +141,7 @@ describe('Combined API Features Translation', () => { }; `; const translatedCode = translateCode(code); - expect(translatedCode).toContain('return "Bearer " + bru.getEnvVar("token");'); + expect(translatedCode).toContain('return "Bearer " + bru.environment.get("token");'); }); it('should handle aliases with object destructuring', () => { @@ -153,8 +153,8 @@ describe('Combined API Features Translation', () => { const translatedCode = translateCode(code); expect(translatedCode).toBe(` - bru.setEnvVar("token", "abc123"); - bru.getVar("userId"); + bru.environment.set("token", "abc123"); + bru.variables.get("userId"); `); }); @@ -169,7 +169,7 @@ describe('Combined API Features Translation', () => { const translatedCode = translateCode(code); expect(translatedCode).toBe(` function getAuthHeader() { - return "Bearer " + bru.getEnvVar("token"); + return "Bearer " + bru.environment.get("token"); } `); }); @@ -354,8 +354,8 @@ describe('Combined API Features Translation', () => { const translatedCode = translateCode(code); expect(translatedCode).toContain('test("Status code is 200", function() { expect(res.getStatus()).to.equal(200); });'); - expect(translatedCode).toContain('bru.setEnvVar("userId", res.getBody().userId);'); - expect(translatedCode).toContain('bru.setVar("token", res.getBody().token);'); + expect(translatedCode).toContain('bru.environment.set("userId", res.getBody().userId);'); + expect(translatedCode).toContain('bru.variables.set("token", res.getBody().token);'); }); // TODO: Restore once UI update fixes are live for setCollectionVar @@ -377,7 +377,7 @@ describe('Combined API Features Translation', () => { `; const translatedCode = translateCode(code); expect(translatedCode).toBe(` - bru.getCollectionVar(bru.getEnvVar('key')) + bru.getCollectionVar(bru.environment.get('key')) test("Status code is 200", function() { expect(res.getStatus()).to.equal(200); }); @@ -393,8 +393,8 @@ describe('Combined API Features Translation', () => { `; const translatedCode = translateCode(code); - expect(translatedCode).toContain('const baseUrl = bru.getEnvVar("baseUrl");'); - expect(translatedCode).toContain('const endpoint = bru.getVar("endpoint");'); + expect(translatedCode).toContain('const baseUrl = bru.environment.get("baseUrl");'); + expect(translatedCode).toContain('const endpoint = bru.variables.get("endpoint");'); expect(translatedCode).toContain('const url = `${baseUrl}/api/${endpoint}`;'); expect(translatedCode).toContain('console.log(`Response status: ${res.getStatus()}`);'); }); @@ -408,9 +408,9 @@ describe('Combined API Features Translation', () => { `; const translatedCode = translateCode(code); - expect(translatedCode).toContain('const getAuthHeader = () => "Bearer " + bru.getEnvVar("token");'); + expect(translatedCode).toContain('const getAuthHeader = () => "Bearer " + bru.environment.get("token");'); expect(translatedCode).toContain('const processItems = items => items.forEach(item => {'); - expect(translatedCode).toContain('bru.setVar(item.key, item.value);'); + expect(translatedCode).toContain('bru.variables.set(item.key, item.value);'); }); it('test', () => { @@ -420,7 +420,7 @@ describe('Combined API Features Translation', () => { `; const translatedCode = translateCode(code); expect(translatedCode).toBe(` - const key = bru.getGlobalEnvVar("key"); + const key = bru.globals.get("key"); `); }); @@ -460,8 +460,8 @@ describe('Combined API Features Translation', () => { const expectedOutput = ` const expectedResponse = { - id: bru.getEnvVar("userId"), - token: bru.getVar("authToken"), + id: bru.environment.get("userId"), + token: bru.variables.get("authToken"), timestamp: new Date().getTime() }; diff --git a/packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/environment.test.js b/packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/environment.test.js index 1060886b8aa..f0cfd414bae 100644 --- a/packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/environment.test.js +++ b/packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/environment.test.js @@ -4,49 +4,49 @@ describe('Environment Variable Translation', () => { it('should translate pm.environment.get', () => { const code = 'pm.environment.get("test");'; const translatedCode = translateCode(code); - expect(translatedCode).toBe('bru.getEnvVar("test");'); + expect(translatedCode).toBe('bru.environment.get("test");'); }); it('should translate pm.environment.set', () => { const code = 'pm.environment.set("test", "value");'; const translatedCode = translateCode(code); - expect(translatedCode).toBe('bru.setEnvVar("test", "value");'); + expect(translatedCode).toBe('bru.environment.set("test", "value");'); }); it('should translate pm.environment.has', () => { const code = 'pm.environment.has("test")'; const translatedCode = translateCode(code); - expect(translatedCode).toBe('bru.getEnvVar("test") !== undefined && bru.getEnvVar("test") !== null'); + expect(translatedCode).toBe('bru.environment.has("test")'); }); it('should translate pm.environment.unset', () => { const code = 'pm.environment.unset("test");'; const translatedCode = translateCode(code); - expect(translatedCode).toBe('bru.deleteEnvVar("test");'); + expect(translatedCode).toBe('bru.environment.unset("test");'); }); it('should translate pm.environment.name', () => { const code = 'pm.environment.name;'; const translatedCode = translateCode(code); - expect(translatedCode).toBe('bru.getEnvName();'); + expect(translatedCode).toBe('bru.environment.name;'); }); it('should handle nested Postman API calls with environment', () => { const code = 'pm.environment.set("computed", pm.variables.get("base") + "-suffix");'; const translatedCode = translateCode(code); - expect(translatedCode).toBe('bru.setEnvVar("computed", bru.getVar("base") + "-suffix");'); + expect(translatedCode).toBe('bru.environment.set("computed", bru.variables.get("base") + "-suffix");'); }); it('should handle JSON operations with environment variables', () => { const code = 'pm.environment.set("user", JSON.stringify({ id: 123, name: "John" }));'; const translatedCode = translateCode(code); - expect(translatedCode).toBe('bru.setEnvVar("user", JSON.stringify({ id: 123, name: "John" }));'); + expect(translatedCode).toBe('bru.environment.set("user", JSON.stringify({ id: 123, name: "John" }));'); }); it('should handle JSON.parse with environment variables', () => { const code = 'const userData = JSON.parse(pm.environment.get("user"));'; const translatedCode = translateCode(code); - expect(translatedCode).toBe('const userData = JSON.parse(bru.getEnvVar("user"));'); + expect(translatedCode).toBe('const userData = JSON.parse(bru.environment.get("user"));'); }); it('should translate pm.environment.name with different access patterns', () => { @@ -58,9 +58,9 @@ describe('Environment Variable Translation', () => { `; const translatedCode = translateCode(code); expect(translatedCode).toBe(` - const envName1 = bru.getEnvName(); - const envName2 = bru.getEnvName(); - console.log(bru.getEnvName()); + const envName1 = bru.environment.name; + const envName2 = bru.environment.name; + console.log(bru.environment.name); `); }); @@ -75,11 +75,11 @@ describe('Environment Variable Translation', () => { `; const translatedCode = translateCode(code); expect(translatedCode).toBe(` - const name = bru.getEnvName(); - const has = bru.getEnvVar("test") !== undefined && bru.getEnvVar("test") !== null; - const set = bru.setEnvVar("test", "value"); - const get = bru.getEnvVar("test"); - const unset = bru.deleteEnvVar("test"); + const name = bru.environment.name; + const has = bru.environment.has("test"); + const set = bru.environment.set("test", "value"); + const get = bru.environment.get("test"); + const unset = bru.environment.unset("test"); `); }); @@ -87,19 +87,19 @@ describe('Environment Variable Translation', () => { it('should translate postman.setEnvironmentVariable', () => { const code = 'postman.setEnvironmentVariable("apiKey", "abc123");'; const translatedCode = translateCode(code); - expect(translatedCode).toBe('bru.setEnvVar("apiKey", "abc123");'); + expect(translatedCode).toBe('bru.environment.set("apiKey", "abc123");'); }); it('should translate postman.getEnvironmentVariable', () => { const code = 'const baseUrl = postman.getEnvironmentVariable("baseUrl");'; const translatedCode = translateCode(code); - expect(translatedCode).toBe('const baseUrl = bru.getEnvVar("baseUrl");'); + expect(translatedCode).toBe('const baseUrl = bru.environment.get("baseUrl");'); }); it('should translate postman.clearEnvironmentVariable', () => { const code = 'postman.clearEnvironmentVariable("tempToken");'; const translatedCode = translateCode(code); - expect(translatedCode).toBe('bru.deleteEnvVar("tempToken");'); + expect(translatedCode).toBe('bru.environment.unset("tempToken");'); }); it('should handle all environment variable methods together', () => { @@ -114,10 +114,10 @@ describe('Environment Variable Translation', () => { `; const translatedCode = translateCode(code); - expect(translatedCode).toContain('const envName = bru.getEnvName();'); - expect(translatedCode).toContain('const hasToken = bru.getEnvVar("token") !== undefined && bru.getEnvVar("token") !== null;'); - expect(translatedCode).toContain('const token = bru.getEnvVar("token");'); - expect(translatedCode).toContain('bru.setEnvVar("timestamp", new Date().toISOString());'); + expect(translatedCode).toContain('const envName = bru.environment.name;'); + expect(translatedCode).toContain('const hasToken = bru.environment.has("token");'); + expect(translatedCode).toContain('const token = bru.environment.get("token");'); + expect(translatedCode).toContain('bru.environment.set("timestamp", new Date().toISOString());'); }); // Additional robust tests for environment variables @@ -129,8 +129,8 @@ describe('Environment Variable Translation', () => { const computedValue = pm.environment.get(prefix + "_" + suffix); `; const translatedCode = translateCode(code); - expect(translatedCode).toContain('bru.setEnvVar(prefix + "_" + suffix, "abc123");'); - expect(translatedCode).toContain('const computedValue = bru.getEnvVar(prefix + "_" + suffix);'); + expect(translatedCode).toContain('bru.environment.set(prefix + "_" + suffix, "abc123");'); + expect(translatedCode).toContain('const computedValue = bru.environment.get(prefix + "_" + suffix);'); }); it('should handle environment variables in complex object structures', () => { @@ -146,11 +146,11 @@ describe('Environment Variable Translation', () => { }; `; const translatedCode = translateCode(code); - expect(translatedCode).toContain('baseUrl: bru.getEnvVar("apiUrl"),'); - expect(translatedCode).toContain('"Authorization": "Bearer " + bru.getEnvVar("token"),'); - expect(translatedCode).toContain('"X-Api-Key": bru.getEnvVar("apiKey") || "default-key"'); - expect(translatedCode).toContain('timeout: parseInt(bru.getEnvVar("timeout") || "5000"),'); - expect(translatedCode).toContain('validate: bru.getEnvVar("validateResponses") !== undefined && bru.getEnvVar("validateResponses") !== null'); + expect(translatedCode).toContain('baseUrl: bru.environment.get("apiUrl"),'); + expect(translatedCode).toContain('"Authorization": "Bearer " + bru.environment.get("token"),'); + expect(translatedCode).toContain('"X-Api-Key": bru.environment.get("apiKey") || "default-key"'); + expect(translatedCode).toContain('timeout: parseInt(bru.environment.get("timeout") || "5000"),'); + expect(translatedCode).toContain('validate: bru.environment.has("validateResponses")'); }); it('should handle environment variables in conditionals correctly', () => { @@ -166,8 +166,8 @@ describe('Environment Variable Translation', () => { } `; const translatedCode = translateCode(code); - expect(translatedCode).toContain('if (bru.getEnvVar("apiKey") !== undefined && bru.getEnvVar("apiKey") !== null) {'); - expect(translatedCode).toContain('if (bru.getEnvVar("apiKey").length > 0) {'); + expect(translatedCode).toContain('if (bru.environment.has("apiKey")) {'); + expect(translatedCode).toContain('if (bru.environment.get("apiKey").length > 0) {'); }); it('should handle multiple levels of environment variable aliasing', () => { @@ -180,9 +180,9 @@ describe('Environment Variable Translation', () => { `; const translatedCode = translateCode(code); expect(translatedCode).toBe(` - bru.setEnvVar("key", "value"); - const value = bru.getEnvVar("key"); - const exists = bru.getEnvVar("key") !== undefined && bru.getEnvVar("key") !== null; + bru.environment.set("key", "value"); + const value = bru.environment.get("key"); + const exists = bru.environment.has("key"); `); }); @@ -202,9 +202,9 @@ describe('Environment Variable Translation', () => { pm.environment.set("tokenExpiry", expiryTime.getTime()); `; const translatedCode = translateCode(code); - expect(translatedCode).toContain('bru.setEnvVar("requestTimestamp", timestamp);'); - expect(translatedCode).toContain('bru.setEnvVar("requestId", uniqueId);'); - expect(translatedCode).toContain('bru.setEnvVar("tokenExpiry", expiryTime.getTime());'); + expect(translatedCode).toContain('bru.environment.set("requestTimestamp", timestamp);'); + expect(translatedCode).toContain('bru.environment.set("requestId", uniqueId);'); + expect(translatedCode).toContain('bru.environment.set("tokenExpiry", expiryTime.getTime());'); }); it('should handle environment variables in try-catch blocks', () => { @@ -219,8 +219,8 @@ describe('Environment Variable Translation', () => { } `; const translatedCode = translateCode(code); - expect(translatedCode).toContain('const configStr = bru.getEnvVar("config");'); - expect(translatedCode).toContain('bru.setEnvVar("configError", error.message);'); + expect(translatedCode).toContain('const configStr = bru.environment.get("config");'); + expect(translatedCode).toContain('bru.environment.set("configError", error.message);'); }); it('should handle legacy environment and pm.setEnvironmentVariable together', () => { @@ -235,8 +235,8 @@ describe('Environment Variable Translation', () => { pm.setEnvironmentVariable("thirdKey", "thirdValue"); `; const translatedCode = translateCode(code); - expect(translatedCode).toContain('bru.setEnvVar("legacyKey", "legacyValue");'); - expect(translatedCode).toContain('const value = bru.getEnvVar("anotherKey");'); - expect(translatedCode).toContain('bru.setEnvVar("thirdKey", "thirdValue");'); + expect(translatedCode).toContain('bru.environment.set("legacyKey", "legacyValue");'); + expect(translatedCode).toContain('const value = bru.environment.get("anotherKey");'); + expect(translatedCode).toContain('bru.environment.set("thirdKey", "thirdValue");'); }); }); diff --git a/packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/legacy-tests-syntax.test.js b/packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/legacy-tests-syntax.test.js index 9f49407208c..e1e007dc55c 100644 --- a/packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/legacy-tests-syntax.test.js +++ b/packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/legacy-tests-syntax.test.js @@ -83,7 +83,7 @@ describe('Legacy Tests[] Syntax Translation', () => { const translatedCode = translateCode(code); expect(translatedCode).toBe(` test("Response matches environment variable", function() { - expect(Boolean(res.getBody().id === bru.getEnvVar("expectedId"))).to.be.true; + expect(Boolean(res.getBody().id === bru.environment.get("expectedId"))).to.be.true; });`); }); @@ -98,7 +98,7 @@ describe('Legacy Tests[] Syntax Translation', () => { // but we can check for key transformations expect(translatedCode).toContain('test("Authentication header is present"'); expect(translatedCode).toContain('test("Data count is correct"'); - expect(translatedCode).toContain('res.getBody().items.length === bru.getVar("expectedCount")'); + expect(translatedCode).toContain('res.getBody().items.length === bru.variables.get("expectedCount")'); }); // Additional robust tests for legacy tests[] syntax @@ -274,10 +274,10 @@ describe('Legacy Tests[] Syntax Translation', () => { expect(translatedCode).toContain('expect(Boolean(res.headerList.has("Content-Type"))).to.be.true;'); expect(translatedCode).toContain('test("Content-Type is JSON", function() {'); 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('const expectedItems = parseInt(bru.environment.get("expectedItemCount"));'); expect(translatedCode).toContain('test("Has correct number of items", function() {'); expect(translatedCode).toContain('expect(Boolean(response.items.length === expectedItems)).to.be.true;'); - expect(translatedCode).toContain('const targetItem = response.items.find(item => item.name === bru.getVar("targetItemName"));'); - expect(translatedCode).toContain('bru.setEnvVar("targetItemId", targetItem.id);'); + expect(translatedCode).toContain('const targetItem = response.items.find(item => item.name === bru.variables.get("targetItemName"));'); + expect(translatedCode).toContain('bru.environment.set("targetItemId", targetItem.id);'); }); }); diff --git a/packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/multiline-syntax.test.js b/packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/multiline-syntax.test.js index 88a3b9db92a..445d6d384f9 100644 --- a/packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/multiline-syntax.test.js +++ b/packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/multiline-syntax.test.js @@ -12,9 +12,9 @@ describe('Multiline Syntax Handling', () => { `; const translatedCode = translateCode(code); expect(translatedCode).toBe(` - const userId = bru.getVar("userId"); - bru.setVar("timestamp", new Date().toISOString()); - const hasToken = bru.hasVar("token"); + const userId = bru.variables.get("userId"); + bru.variables.set("timestamp", new Date().toISOString()); + const hasToken = bru.variables.has("token"); `); }); @@ -29,8 +29,8 @@ describe('Multiline Syntax Handling', () => { `; const translatedCode = translateCode(code); expect(translatedCode).toBe(` - const baseUrl = bru.getEnvVar("baseUrl"); - bru.setEnvVar("requestTime", Date.now()); + const baseUrl = bru.environment.get("baseUrl"); + bru.environment.set("requestTime", Date.now()); `); }); @@ -62,7 +62,7 @@ describe('Multiline Syntax Handling', () => { `; const translatedCode = translateCode(code); expect(translatedCode).toBe(` - if (bru.getEnvVar("apiKey") !== undefined && bru.getEnvVar("apiKey") !== null) { + if (bru.environment.has("apiKey")) { console.log("API Key exists"); } `); @@ -152,16 +152,16 @@ describe('Multiline Syntax Handling', () => { const translatedCode = translateCode(code); expect(translatedCode).toBe(` // Normal syntax - const normalVar = bru.getVar("normal"); + const normalVar = bru.variables.get("normal"); // Multiline syntax - const multilineVar = bru.getVar("multiline"); + const multilineVar = bru.variables.get("multiline"); // Normal syntax again - bru.setVar("normalSet", "value"); + bru.variables.set("normalSet", "value"); // Multiline syntax again - bru.setVar("multilineSet", "value"); + bru.variables.set("multilineSet", "value"); `); }); @@ -261,17 +261,17 @@ describe('Multiline Syntax Handling', () => { const translatedCode = translateCode(code); - expect(translatedCode).toContain('const baseUrl = bru.getEnvVar("baseUrl")'); - expect(translatedCode).toContain('const apiKey = bru.getEnvVar("apiKey")'); - expect(translatedCode).toContain('const userId = bru.getEnvVar("userId")'); + expect(translatedCode).toContain('const baseUrl = bru.environment.get("baseUrl")'); + expect(translatedCode).toContain('const apiKey = bru.environment.get("apiKey")'); + expect(translatedCode).toContain('const userId = bru.environment.get("userId")'); // Check variables translations - expect(translatedCode).toContain('bru.setVar("testId", "test-" + Date.now())'); - expect(translatedCode).toContain('bru.setVar("timestamp", new Date().toISOString())'); + expect(translatedCode).toContain('bru.variables.set("testId", "test-" + Date.now())'); + expect(translatedCode).toContain('bru.variables.set("timestamp", new Date().toISOString())'); // Check complex conditionals - expect(translatedCode).toContain('if (bru.getEnvVar("apiKey") !== undefined && bru.getEnvVar("apiKey") !== null &&'); - expect(translatedCode).toContain('bru.hasVar("testId"))'); + expect(translatedCode).toContain('if (bru.environment.has("apiKey") &&'); + expect(translatedCode).toContain('bru.variables.has("testId"))'); // Check response testing expect(translatedCode).toContain('expect(res.getStatus()).to.equal(200)'); diff --git a/packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/postman-references.test.js b/packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/postman-references.test.js index a7618561f8a..8ce349a1898 100644 --- a/packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/postman-references.test.js +++ b/packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/postman-references.test.js @@ -5,7 +5,7 @@ describe('Postman to PM References Conversion', () => { it('should convert basic postman references to pm', () => { const code = 'postman.setEnvironmentVariable("key", "value");'; const translatedCode = translateCode(code); - expect(translatedCode).toBe('bru.setEnvVar("key", "value");'); + expect(translatedCode).toBe('bru.environment.set("key", "value");'); // The key part is that it should convert postman.* to pm.* internally before // translating to bru.* APIs }); @@ -13,7 +13,7 @@ describe('Postman to PM References Conversion', () => { it('should convert postman variable access to pm', () => { const code = 'const value = postman.getEnvironmentVariable("key");'; const translatedCode = translateCode(code); - expect(translatedCode).toBe('const value = bru.getEnvVar("key");'); + expect(translatedCode).toBe('const value = bru.environment.get("key");'); }); it('should handle postman variable assignments', () => { @@ -22,8 +22,8 @@ describe('Postman to PM References Conversion', () => { const baseUrl = postman.environment.get("baseUrl"); `; const translatedCode = translateCode(code); - expect(translatedCode).toContain('const envVar = bru.getEnvVar("apiKey");'); - expect(translatedCode).toContain('const baseUrl = bru.getEnvVar("baseUrl");'); + expect(translatedCode).toContain('const envVar = bru.environment.get("apiKey");'); + expect(translatedCode).toContain('const baseUrl = bru.environment.get("baseUrl");'); }); // More complex patterns @@ -40,8 +40,8 @@ describe('Postman to PM References Conversion', () => { `; const translatedCode = translateCode(code); - expect(translatedCode).toContain('const apiKey = bru.getEnvVar("apiKey");'); - expect(translatedCode).toContain('const baseUrl = bru.getEnvVar("baseUrl");'); + expect(translatedCode).toContain('const apiKey = bru.environment.get("apiKey");'); + expect(translatedCode).toContain('const baseUrl = bru.environment.get("baseUrl");'); expect(translatedCode).toContain('test("Status code is 200", function() {'); expect(translatedCode).toContain('expect(res.getStatus()).to.equal(200);'); }); @@ -53,7 +53,7 @@ describe('Postman to PM References Conversion', () => { `; const translatedCode = translateCode(code); - expect(translatedCode).toContain('bru.setEnvVar("key", "value");'); + expect(translatedCode).toContain('bru.environment.set("key", "value");'); }); // Complex control flows @@ -69,10 +69,10 @@ describe('Postman to PM References Conversion', () => { `; const translatedCode = translateCode(code); - expect(translatedCode).toContain('if (bru.getEnvVar("isProduction") === "true") {'); - expect(translatedCode).toContain('const apiUrl = bru.getEnvVar("prodUrl");'); + expect(translatedCode).toContain('if (bru.environment.get("isProduction") === "true") {'); + expect(translatedCode).toContain('const apiUrl = bru.environment.get("prodUrl");'); expect(translatedCode).toContain('bru.setNextRequest("Production Flow");'); - expect(translatedCode).toContain('const apiUrl = bru.getEnvVar("devUrl");'); + expect(translatedCode).toContain('const apiUrl = bru.environment.get("devUrl");'); expect(translatedCode).toContain('bru.setNextRequest("Development Flow");'); }); @@ -90,7 +90,7 @@ describe('Postman to PM References Conversion', () => { const translatedCode = translateCode(code); expect(translatedCode).toContain('const responseCode = res.getStatus();'); expect(translatedCode).toContain('const responseBody = res.getBody();'); - expect(translatedCode).toContain('bru.setEnvVar("lastResponseCode", responseCode);'); + expect(translatedCode).toContain('bru.environment.set("lastResponseCode", responseCode);'); }); // Postman in string literals should be untouched @@ -124,8 +124,8 @@ describe('Postman to PM References Conversion', () => { const translatedCode = translateCode(code); // Should handle the aliases properly - expect(translatedCode).toContain('const apiKey = bru.getEnvVar("apiKey");'); - expect(translatedCode).toContain('const userId = bru.getEnvVar("userId");'); + expect(translatedCode).toContain('const apiKey = bru.environment.get("apiKey");'); + expect(translatedCode).toContain('const userId = bru.environment.get("userId");'); expect(translatedCode).toContain('test("Response is valid", function() {'); expect(translatedCode).toContain('expect(code).to.equal(200);'); }); diff --git a/packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/request.test.js b/packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/request.test.js index 979ad88c16e..84909b0c697 100644 --- a/packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/request.test.js +++ b/packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/request.test.js @@ -102,8 +102,8 @@ describe('Request Translation', () => { `; const translatedCode = translateCode(code); expect(translatedCode).toContain('const requestData = req.getBody();'); - expect(translatedCode).toContain('bru.setVar("lastRequestBody", JSON.stringify(requestData));'); - expect(translatedCode).toContain('bru.setEnvVar("lastContentType", contentType);'); + expect(translatedCode).toContain('bru.variables.set("lastRequestBody", JSON.stringify(requestData));'); + expect(translatedCode).toContain('bru.environment.set("lastContentType", contentType);'); }); it('should translate legacy request.* properties', () => { diff --git a/packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/response.test.js b/packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/response.test.js index 176b9632e38..167a939e441 100644 --- a/packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/response.test.js +++ b/packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/response.test.js @@ -238,7 +238,7 @@ describe('Response Translation', () => { expect(translatedCode).not.toContain('const resp = pm.response;'); expect(translatedCode).toContain('const data = res.getBody();'); - expect(translatedCode).toContain('bru.setEnvVar("userId", data.user.id);'); + expect(translatedCode).toContain('bru.environment.set("userId", data.user.id);'); }); it('should handle all response property methods together', () => { @@ -338,7 +338,7 @@ describe('Response Translation', () => { expect(translatedCode).toContain('const { id, name, items } = res.getBody();'); expect(translatedCode).toContain('const [first, second] = items;'); - expect(translatedCode).toContain('bru.setEnvVar("userId", id);'); + expect(translatedCode).toContain('bru.environment.set("userId", id);'); }); it('should handle response in complex conditionals', () => { @@ -364,7 +364,7 @@ describe('Response Translation', () => { expect(translatedCode).toContain('if (res.getStatus() >= 200 && res.getStatus() < 300) {'); 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('bru.environment.set("authToken", data.token);'); expect(translatedCode).toContain('} else if (res.getStatus() === 404) {'); expect(translatedCode).toContain('console.error("Request failed with status:", res.getStatus());'); }); @@ -383,9 +383,9 @@ describe('Response Translation', () => { const translatedCode = translateCode(code); expect(translatedCode).toContain('const data = res.getBody();'); - expect(translatedCode).toContain('bru.setEnvVar("userData", JSON.stringify(data.user));'); + expect(translatedCode).toContain('bru.environment.set("userData", JSON.stringify(data.user));'); expect(translatedCode).toContain('const text = JSON.stringify(res.getBody());'); - expect(translatedCode).toContain('bru.setEnvVar("rawResponse", text);'); + expect(translatedCode).toContain('bru.environment.set("rawResponse", text);'); }); it('should handle JSON path style access to response data', () => { @@ -403,7 +403,7 @@ describe('Response Translation', () => { expect(translatedCode).toContain('const userId = data.user.id;'); expect(translatedCode).toContain('const userEmail = data.user.contact.email;'); expect(translatedCode).toContain('const firstItem = data.items[0];'); - expect(translatedCode).toContain('bru.setEnvVar("userId", userId);'); + expect(translatedCode).toContain('bru.environment.set("userId", userId);'); }); it('should handle template literals with response data', () => { @@ -417,7 +417,7 @@ describe('Response Translation', () => { expect(translatedCode).toContain('const data = res.getBody();'); expect(translatedCode).toContain('const welcomeMessage = `Hello, ${data.user.name}! Your ID is ${data.user.id}.`;'); - expect(translatedCode).toContain('bru.setEnvVar("welcomeMessage", welcomeMessage);'); + expect(translatedCode).toContain('bru.environment.set("welcomeMessage", welcomeMessage);'); }); it('should handle response processing in arrow functions', () => { @@ -435,7 +435,7 @@ describe('Response Translation', () => { expect(translatedCode).toContain('const items = res.getBody().items;'); expect(translatedCode).toContain('return items.map(item => item.id);'); expect(translatedCode).toContain('const itemIds = processItems();'); - expect(translatedCode).toContain('bru.setEnvVar("itemIds", JSON.stringify(itemIds));'); + expect(translatedCode).toContain('bru.environment.set("itemIds", JSON.stringify(itemIds));'); }); it('should handle complex inline operations with response data', () => { @@ -454,8 +454,8 @@ describe('Response Translation', () => { expect(translatedCode).toContain('const totalValue = items.reduce((sum, item) => sum + item.price, 0);'); expect(translatedCode).toContain('const highValueItems = items.filter(item => item.price > 100);'); expect(translatedCode).toContain('const itemNames = items.map(item => item.name);'); - expect(translatedCode).toContain('bru.setEnvVar("totalValue", totalValue);'); - expect(translatedCode).toContain('bru.setEnvVar("highValueItemCount", highValueItems.length);'); + expect(translatedCode).toContain('bru.environment.set("totalValue", totalValue);'); + expect(translatedCode).toContain('bru.environment.set("highValueItemCount", highValueItems.length);'); }); it('should handle complex test structure with pm.response.to.have.header', () => { diff --git a/packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/testing-framework.test.js b/packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/testing-framework.test.js index a1f189b6f94..57d4df8b7fc 100644 --- a/packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/testing-framework.test.js +++ b/packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/testing-framework.test.js @@ -30,7 +30,7 @@ describe('Testing Framework Translation', () => { const translatedCode = translateCode(code); expect(translatedCode).toBe(` test("Check environment and call successful", function () { - expect(bru.getEnvName()).to.equal("ENVIRONMENT_NAME"); + expect(bru.environment.name).to.equal("ENVIRONMENT_NAME"); expect(res.getStatus()).to.equal(200); });`); }); @@ -66,7 +66,7 @@ describe('Testing Framework Translation', () => { expect(translatedCode).toContain('test("Auth flow works", function() {'); expect(translatedCode).toContain('const response = res.getBody();'); expect(translatedCode).toContain('expect(response.authenticated).to.be.true;'); - expect(translatedCode).toContain('bru.setEnvVar("userId", response.user.id);'); + expect(translatedCode).toContain('bru.environment.set("userId", response.user.id);'); }); // TODO: Restore once UI update fixes are live for setCollectionVar @@ -164,7 +164,7 @@ describe('Testing Framework Translation', () => { `; const translatedCode = translateCode(code); - expect(translatedCode).toContain('const endpoint = bru.getVar("currentEndpoint");'); + expect(translatedCode).toContain('const endpoint = bru.variables.get("currentEndpoint");'); expect(translatedCode).toContain('test(`${endpoint} returns correct data`, function() {'); expect(translatedCode).toContain('const responseJson = res.getBody();'); expect(translatedCode).toContain('expect(responseJson).to.be.an(\'object\');'); @@ -400,6 +400,6 @@ describe('Testing Framework Translation', () => { expect(translatedCode).toContain('expect(res.getStatus()).to.equal(200);'); expect(translatedCode).toContain('test("Related users validation", function() {'); expect(translatedCode).toContain('test(`User at index ${index}`, function() {'); - expect(translatedCode).toContain('bru.setEnvVar("validUsers", JSON.stringify(validUsers));'); + expect(translatedCode).toContain('bru.environment.set("validUsers", JSON.stringify(validUsers));'); }); }); diff --git a/packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/variable-chaining.test.js b/packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/variable-chaining.test.js index b2d6a58f182..f3fa2f76667 100644 --- a/packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/variable-chaining.test.js +++ b/packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/variable-chaining.test.js @@ -37,7 +37,7 @@ describe('Variable Chaining Resolution', () => { expect(translatedCode).not.toContain('const respVar'); expect(translatedCode).not.toContain('const envVar'); expect(translatedCode).toContain('const statusCode = res.getStatus();'); - expect(translatedCode).toContain('const envValue = bru.getEnvVar("key");'); + expect(translatedCode).toContain('const envValue = bru.environment.get("key");'); // Check that unrelated variables are preserved expect(translatedCode).toContain('const unrelatedVar = "some value";'); @@ -82,7 +82,7 @@ describe('Variable Chaining Resolution', () => { // References to Postman objects should be properly translated expect(translatedCode).toContain('const statusCode = res.getStatus();'); - expect(translatedCode).toContain('const baseUrl = bru.getEnvVar("baseUrl");'); + expect(translatedCode).toContain('const baseUrl = bru.environment.get("baseUrl");'); // Console logs with regular variables should be preserved expect(translatedCode).toContain('console.log("Counter value:", counter);'); diff --git a/packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/variables.test.js b/packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/variables.test.js index 30e12f2496b..a6bcaf05a87 100644 --- a/packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/variables.test.js +++ b/packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/variables.test.js @@ -6,21 +6,21 @@ describe('Variables Translation', () => { const code = 'pm.variables.get("test");'; const translatedCode = translateCode(code); - expect(translatedCode).toBe('bru.getVar("test");'); + expect(translatedCode).toBe('bru.variables.get("test");'); }); it('should translate pm.variables.set', () => { const code = 'pm.variables.set("test", "value");'; const translatedCode = translateCode(code); - expect(translatedCode).toBe('bru.setVar("test", "value");'); + expect(translatedCode).toBe('bru.variables.set("test", "value");'); }); it('should translate pm.variables.has', () => { const code = 'pm.variables.has("userId");'; const translatedCode = translateCode(code); - expect(translatedCode).toBe('bru.hasVar("userId");'); + expect(translatedCode).toBe('bru.variables.has("userId");'); }); it('should translate pm.variables.replaceIn', () => { @@ -98,14 +98,14 @@ describe('Variables Translation', () => { const code = 'pm.globals.get("test");'; const translatedCode = translateCode(code); - expect(translatedCode).toBe('bru.getGlobalEnvVar("test");'); + expect(translatedCode).toBe('bru.globals.get("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");'); + expect(translatedCode).toBe('bru.globals.set("test", "value");'); }); // Alias tests for variables @@ -119,9 +119,9 @@ describe('Variables Translation', () => { const translatedCode = translateCode(code); expect(translatedCode).toBe(` - const has = bru.hasVar("test"); - const set = bru.setVar("test", "value"); - const get = bru.getVar("test"); + const has = bru.variables.has("test"); + const set = bru.variables.set("test", "value"); + const get = bru.variables.get("test"); `); }); @@ -154,8 +154,8 @@ describe('Variables Translation', () => { const translatedCode = translateCode(code); expect(translatedCode).toBe(` - const get = bru.getGlobalEnvVar("test"); - const set = bru.setGlobalEnvVar("test", "value"); + const get = bru.globals.get("test"); + const set = bru.globals.set("test", "value"); `); }); @@ -164,7 +164,7 @@ describe('Variables Translation', () => { const code = 'const userStatus = pm.variables.has("userId") ? "logged-in" : "guest";'; const translatedCode = translateCode(code); - expect(translatedCode).toBe('const userStatus = bru.hasVar("userId") ? "logged-in" : "guest";'); + expect(translatedCode).toBe('const userStatus = bru.variables.has("userId") ? "logged-in" : "guest";'); }); it('should handle all variable methods together', () => { @@ -178,9 +178,9 @@ describe('Variables Translation', () => { `; const translatedCode = translateCode(code); - expect(translatedCode).toContain('const hasUserId = bru.hasVar("userId");'); - expect(translatedCode).toContain('const userId = bru.getVar("userId");'); - expect(translatedCode).toContain('bru.setVar("requestTime", new Date().toISOString());'); + expect(translatedCode).toContain('const hasUserId = bru.variables.has("userId");'); + expect(translatedCode).toContain('const userId = bru.variables.get("userId");'); + expect(translatedCode).toContain('bru.variables.set("requestTime", new Date().toISOString());'); }); // TODO: Restore once UI update fixes are live for setCollectionVar/deleteCollectionVar @@ -207,7 +207,7 @@ 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.setCollectionVar("fullPath", bru.getEnvVar("baseUrl") + bru.getVar("endpoint"));'); + expect(translatedCode).toBe('bru.setCollectionVar("fullPath", bru.environment.get("baseUrl") + bru.variables.get("endpoint"));'); }); // replaceIn tests for different variable scopes @@ -244,16 +244,14 @@ describe('Variables Translation', () => { const code = 'pm.globals.has("token");'; const translatedCode = translateCode(code); - expect(translatedCode).toContain('bru.getGlobalEnvVar("token") !== undefined'); - expect(translatedCode).toContain('bru.getGlobalEnvVar("token") !== null'); + expect(translatedCode).toBe('bru.globals.has("token");'); }); it('should translate pm.globals.has in conditional', () => { const code = 'if (pm.globals.has("authToken")) { console.log("Token exists"); }'; const translatedCode = translateCode(code); - expect(translatedCode).toContain('bru.getGlobalEnvVar("authToken") !== undefined'); - expect(translatedCode).toContain('bru.getGlobalEnvVar("authToken") !== null'); + expect(translatedCode).toContain('bru.globals.has("authToken")'); expect(translatedCode).toContain('console.log("Token exists");'); }); @@ -261,6 +259,6 @@ describe('Variables Translation', () => { const code = 'const hasGlobal = pm.globals.has("config");'; const translatedCode = translateCode(code); - expect(translatedCode).toContain('const hasGlobal = bru.getGlobalEnvVar("config") !== undefined && bru.getGlobalEnvVar("config") !== null'); + expect(translatedCode).toBe('const hasGlobal = bru.globals.has("config");'); }); }); diff --git a/packages/bruno-js/src/bru.js b/packages/bruno-js/src/bru.js index f728f15c02f..7923e277d8a 100644 --- a/packages/bruno-js/src/bru.js +++ b/packages/bruno-js/src/bru.js @@ -4,6 +4,7 @@ const { interpolate: _interpolate } = require('@usebruno/common'); const { sendRequest, createSendRequest } = require('@usebruno/requests').scripting; const { jar: createCookieJar, getCookiesForUrl } = require('@usebruno/requests').cookies; const CookieList = require('./cookie-list'); +const VariableList = require('./variable-list'); const variableNameRegex = /^[\w-.]*$/; @@ -67,6 +68,49 @@ class Bru { createCookieJar, getCookiesForUrl }); + + const validateKey = (key) => { + if (variableNameRegex.test(key) === false) { + throw new Error( + `Variable name: "${key}" contains invalid characters!` + + ' Names must only contain alpha-numeric characters, "-", "_", "."' + ); + } + }; + + this.variables = new VariableList(this.runtimeVariables, { + interpolateFn: (val) => this.interpolate(val), + validateKey + }); + + this.environment = new VariableList(this.envVariables, { + interpolateFn: (val) => this.interpolate(val), + validateKey, + filterKeys: ['__name__'] + }); + Object.defineProperty(this.environment, 'name', { + get: () => this.envVariables.__name__, + enumerable: true + }); + + this.globals = new VariableList(this.globalEnvironmentVariables, { + interpolateFn: (val) => this.interpolate(val), + validateKey, + filterKeys: ['__name__'] + }); + Object.defineProperty(this.globals, 'name', { + get: () => this.globalEnvironmentVariables.__name__, + enumerable: true + }); + // TODO: globals.unset/clear work in the request lifecycle but do not update the UI. + // Re-enable once the UI sync issue is resolved. + this.globals.unset = () => { + throw new Error('globals.unset is not implemented yet'); + }; + this.globals.clear = () => { + throw new Error('globals.clear is not implemented yet'); + }; + // Holds variables that are marked as persistent by scripts this.persistentEnvVariables = {}; // Holds credential IDs to be reset after script execution @@ -161,6 +205,10 @@ class Bru { return this.envVariables.__name__; } + getGlobalEnvName() { + return this.globalEnvironmentVariables.__name__; + } + getProcessEnv(key) { return this.processEnvVars[key]; } diff --git a/packages/bruno-js/src/sandbox/quickjs/shims/bru.js b/packages/bruno-js/src/sandbox/quickjs/shims/bru.js index 708ef755ee7..45d63f60150 100644 --- a/packages/bruno-js/src/sandbox/quickjs/shims/bru.js +++ b/packages/bruno-js/src/sandbox/quickjs/shims/bru.js @@ -18,6 +18,12 @@ const addBruShimToContext = (vm, bru) => { vm.setProp(bruObject, 'getEnvName', getEnvName); getEnvName.dispose(); + let getGlobalEnvName = vm.newFunction('getGlobalEnvName', function () { + return marshallToVm(bru.getGlobalEnvName(), vm); + }); + vm.setProp(bruObject, 'getGlobalEnvName', getGlobalEnvName); + getGlobalEnvName.dispose(); + let getCollectionName = vm.newFunction('getCollectionName', function () { return marshallToVm(bru.getCollectionName(), vm); }); @@ -500,6 +506,43 @@ const addBruShimToContext = (vm, bru) => { vm.setProp(bruObject, 'cookies', bruCookiesObject); bruCookiesObject.dispose(); + // ── bru.variables (runtime variables) ───────────────────────────────── + let bruVariablesObject = vm.newObject(); + const { evalCode: variablesEvalCode } = createPropertyListBridge(vm, bru.variables, bruVariablesObject, { + globalPath: 'globalThis.bru.variables', + syncReadMethods: ['has'], + syncReadObjectMethods: ['get', 'toObject'], + syncWriteMethods: ['set', 'unset', 'clear'], + withIterators: false + }); + vm.setProp(bruObject, 'variables', bruVariablesObject); + bruVariablesObject.dispose(); + + // ── bru.environment (active collection environment) ─────────────────── + let bruEnvironmentObject = vm.newObject(); + const { evalCode: environmentEvalCode } = createPropertyListBridge(vm, bru.environment, bruEnvironmentObject, { + globalPath: 'globalThis.bru.environment', + syncReadMethods: ['has'], + syncReadObjectMethods: ['get', 'toObject'], + syncWriteMethods: ['set', 'unset', 'clear'], + withIterators: false + }); + vm.setProp(bruObject, 'environment', bruEnvironmentObject); + bruEnvironmentObject.dispose(); + + // ── bru.globals (active global environment) ─────────────────────────── + // TODO: Add 'unset' and 'clear' to syncWriteMethods once the UI sync issue is resolved. + let bruGlobalsObject = vm.newObject(); + const { evalCode: globalsEvalCode } = createPropertyListBridge(vm, bru.globals, bruGlobalsObject, { + globalPath: 'globalThis.bru.globals', + syncReadMethods: ['has'], + syncReadObjectMethods: ['get', 'toObject'], + syncWriteMethods: ['set'], + withIterators: false + }); + vm.setProp(bruObject, 'globals', bruGlobalsObject); + bruGlobalsObject.dispose(); + vm.setProp(bruObject, 'runner', bruRunnerObject); vm.setProp(vm.global, 'bru', bruObject); bruObject.dispose(); @@ -536,6 +579,26 @@ const addBruShimToContext = (vm, bru) => { ${cookiesEvalCode} } + { + ${variablesEvalCode} + } + + { + ${environmentEvalCode} + } + Object.defineProperty(globalThis.bru.environment, 'name', { + get: () => globalThis.bru.getEnvName(), + enumerable: true + }); + + { + ${globalsEvalCode} + } + Object.defineProperty(globalThis.bru.globals, 'name', { + get: () => globalThis.bru.getGlobalEnvName(), + enumerable: true + }); + globalThis.bru.cookies.jar = () => { const _jar = globalThis.bru.cookies._jar(); diff --git a/packages/bruno-js/src/sandbox/quickjs/utils/property-list-bridge.js b/packages/bruno-js/src/sandbox/quickjs/utils/property-list-bridge.js index d8162c2ddbe..ce6d8b3d1d7 100644 --- a/packages/bruno-js/src/sandbox/quickjs/utils/property-list-bridge.js +++ b/packages/bruno-js/src/sandbox/quickjs/utils/property-list-bridge.js @@ -72,6 +72,7 @@ const createAsyncBridge = (vm, targetObj, propName, nativeMethod) => { * @param {string} options.globalPath - Global path in QuickJS (e.g. 'globalThis.bru.cookies') * @param {string[]} [options.syncReadMethods] - Methods that return primitive values * @param {string[]} [options.syncReadObjectMethods] - Methods that return objects (need cleanCircularJson) + * @param {string[]} [options.syncWriteMethods] - Sync write methods (call native, return undefined) * @param {string[]} [options.asyncWriteMethods] - Async write methods (use _prefix bridge) * @param {boolean} [options.withIterators] - Whether to add each/find/filter/map/reduce * @returns {{ evalCode: string }} - JavaScript code to eval in the VM for async wrappers and iterators @@ -104,7 +105,7 @@ const createPropertyListBridge = (vm, nativeList, targetObj, options) => { fn.consume((handle) => vm.setProp(targetObj, methodName, handle)); } - // Sync write methods — void return, just call and discard + // Sync write methods — call native method, return undefined for (const methodName of syncWriteMethods) { const fn = vm.newFunction(methodName, (...vmArgs) => { const args = vmArgs.map((a) => vm.dump(a)); diff --git a/packages/bruno-js/src/variable-list.js b/packages/bruno-js/src/variable-list.js new file mode 100644 index 00000000000..64473ff943d --- /dev/null +++ b/packages/bruno-js/src/variable-list.js @@ -0,0 +1,125 @@ +const variableNameRegex = /^[\w-.]*$/; + +/** + * VariableList — the `bru.variables` / `bru.environment` / `bru.globals` API in scripts. + * + * A standalone key-value store that wraps a plain `{ key: value }` object + * (e.g. runtimeVariables, envVariables, globalEnvironmentVariables) and exposes + * the standard variable scope interface: get, set, has, unset, clear, toObject. + * + * Write methods mutate the original object in place. Reads always reflect + * the latest state (including mutations made through the legacy bru.setVar() / bru.setEnvVar() paths). + * + * @example + * const list = new VariableList(runtimeVariables, { + * interpolateFn: (val) => bru.interpolate(val) + * }); + * list.set('host', 'example.com'); + * list.get('host'); // 'example.com' (interpolated) + * list.has('host'); // true + * list.toObject(); // { host: 'example.com' } + * list.unset('host'); + */ +class VariableList { + /** + * @param {object} variablesObj - The plain { key: value } object to wrap (mutated in place) + * @param {object} [options] + * @param {Function} [options.interpolateFn] - Interpolation function (bru.interpolate) + * @param {Function} [options.validateKey] - Custom key validation function (throws on invalid) + * @param {string[]} [options.filterKeys] - Internal keys to exclude from reads (e.g. ['__name__']) + */ + constructor(variablesObj, { interpolateFn, validateKey, filterKeys } = {}) { + this._variablesObj = variablesObj; + this._interpolateFn = interpolateFn || ((v) => v); + this._validateKeyFn = validateKey || null; + this._filterKeys = filterKeys || []; + } + + // ── Read methods ──────────────────────────────────────────────────── + + /** + * Get the interpolated value of a variable by key. + * @param {string} key + * @returns {*} The interpolated value, or undefined if not found + */ + get(key) { + return this._interpolateFn(this._variablesObj[key]); + } + + /** + * Check if a variable exists by key. + * Returns false for keys in filterKeys. + * @param {string} key + * @returns {boolean} + */ + has(key) { + if (this._filterKeys.includes(key)) return false; + return key in this._variablesObj; + } + + /** + * Convert all variables to a plain { key: value } object. + * Excludes keys listed in filterKeys. + * @returns {object} + */ + toObject() { + const result = {}; + for (const [k, v] of Object.entries(this._variablesObj)) { + if (!this._filterKeys.includes(k)) { + result[k] = v; + } + } + return result; + } + + // ── Write methods ─────────────────────────────────────────────────── + + /** + * Set a variable. Validates the key name and mutates the underlying object. + * @param {string} key + * @param {*} value + */ + set(key, value) { + if (!key) { + throw new Error('Creating a variable without specifying a name is not allowed.'); + } + this.#validateKey(key); + this._variablesObj[key] = value; + } + + /** + * Remove a variable by key. + * @param {string} key + */ + unset(key) { + delete this._variablesObj[key]; + } + + /** + * Remove all variables. Preserves keys listed in filterKeys. + */ + clear() { + for (const key of Object.keys(this._variablesObj)) { + if (!this._filterKeys.includes(key)) { + delete this._variablesObj[key]; + } + } + } + + // ── Internal helpers ──────────────────────────────────────────────── + + #validateKey(key) { + if (this._validateKeyFn) { + this._validateKeyFn(key); + return; + } + if (variableNameRegex.test(key) === false) { + throw new Error( + `Variable name: "${key}" contains invalid characters!` + + ' Names must only contain alpha-numeric characters, "-", "_", "."' + ); + } + } +} + +module.exports = VariableList; diff --git a/packages/bruno-js/tests/variable-list.spec.js b/packages/bruno-js/tests/variable-list.spec.js new file mode 100644 index 00000000000..8c94719c38e --- /dev/null +++ b/packages/bruno-js/tests/variable-list.spec.js @@ -0,0 +1,175 @@ +const VariableList = require('../src/variable-list'); + +describe('VariableList', () => { + // ── Basic CRUD ──────────────────────────────────────────────────────── + + describe('get / set / has / unset', () => { + let vars; + let list; + + beforeEach(() => { + vars = { host: 'example.com', port: '8080' }; + list = new VariableList(vars, { + interpolateFn: (val) => val + }); + }); + + test('get() returns value from underlying object', () => { + expect(list.get('host')).toBe('example.com'); + expect(list.get('port')).toBe('8080'); + }); + + test('get() returns undefined for missing key', () => { + expect(list.get('missing')).toBeUndefined(); + }); + + test('get() uses interpolateFn', () => { + const interpolated = new VariableList({ url: '{{host}}/api' }, { + interpolateFn: (val) => typeof val === 'string' ? val.replace('{{host}}', 'example.com') : val + }); + expect(interpolated.get('url')).toBe('example.com/api'); + }); + + test('set() writes to underlying object', () => { + list.set('newKey', 'newValue'); + expect(vars.newKey).toBe('newValue'); + expect(list.get('newKey')).toBe('newValue'); + }); + + test('set() throws on empty key', () => { + expect(() => list.set('', 'value')).toThrow('without specifying a name'); + }); + + test('set() validates key format', () => { + expect(() => list.set('invalid key!', 'value')).toThrow('invalid characters'); + }); + + test('set() allows valid key characters', () => { + list.set('my-var_name.v2', 'ok'); + expect(vars['my-var_name.v2']).toBe('ok'); + }); + + test('has() returns true for existing key', () => { + expect(list.has('host')).toBe(true); + }); + + test('has() returns false for missing key', () => { + expect(list.has('missing')).toBe(false); + }); + + test('unset() removes key from underlying object', () => { + list.unset('host'); + expect(vars.host).toBeUndefined(); + expect(list.has('host')).toBe(false); + }); + + test('unset() is a no-op for missing key', () => { + list.unset('nonexistent'); + expect(Object.keys(vars)).toEqual(['host', 'port']); + }); + }); + + // ── clear ───────────────────────────────────────────────────────────── + + describe('clear', () => { + test('removes all keys from underlying object', () => { + const vars = { a: '1', b: '2', c: '3' }; + const list = new VariableList(vars); + list.clear(); + expect(Object.keys(vars)).toEqual([]); + }); + + test('preserves filterKeys', () => { + const vars = { __name__: 'dev', host: 'example.com', port: '8080' }; + const list = new VariableList(vars, { filterKeys: ['__name__'] }); + list.clear(); + expect(vars.__name__).toBe('dev'); + expect(vars.host).toBeUndefined(); + expect(vars.port).toBeUndefined(); + }); + }); + + // ── toObject ─────────────────────────────────────────────────────────── + + describe('toObject', () => { + test('toObject() returns plain { key: value } map', () => { + const list = new VariableList({ a: '1', b: '2', c: '3' }); + expect(list.toObject()).toEqual({ a: '1', b: '2', c: '3' }); + }); + + test('toObject() excludes filterKeys', () => { + const list = new VariableList( + { __name__: 'dev', host: 'example.com' }, + { filterKeys: ['__name__'] } + ); + expect(list.toObject()).toEqual({ host: 'example.com' }); + }); + }); + + // ── filterKeys ──────────────────────────────────────────────────────── + + describe('filterKeys', () => { + let vars; + let list; + + beforeEach(() => { + vars = { __name__: 'dev', host: 'example.com', port: '8080' }; + list = new VariableList(vars, { filterKeys: ['__name__'] }); + }); + + test('toObject() excludes filtered keys', () => { + expect(list.toObject()).toEqual({ host: 'example.com', port: '8080' }); + }); + + test('has() returns false for filtered keys', () => { + expect(list.has('__name__')).toBe(false); + expect(list.has('host')).toBe(true); + }); + + test('get() still returns filtered keys (direct access)', () => { + // get() reads directly from the object, filterKeys only affects has/toObject/toJSON/clear + const interpolated = new VariableList(vars, { + interpolateFn: (v) => v, + filterKeys: ['__name__'] + }); + expect(interpolated.get('__name__')).toBe('dev'); + }); + }); + + // ── Cross-path mutation visibility ──────────────────────────────────── + + describe('shared underlying object', () => { + test('mutations via VariableList are visible on the original object', () => { + const vars = { x: '10' }; + const list = new VariableList(vars); + list.set('y', '20'); + expect(vars.y).toBe('20'); + list.unset('x'); + expect(vars.x).toBeUndefined(); + }); + + test('mutations on the original object are visible via VariableList', () => { + const vars = { x: '10' }; + const list = new VariableList(vars, { interpolateFn: (v) => v }); + vars.y = '20'; + expect(list.get('y')).toBe('20'); + expect(list.has('y')).toBe(true); + delete vars.x; + expect(list.has('x')).toBe(false); + }); + }); + + // ── Custom validateKey ──────────────────────────────────────────────── + + describe('custom validateKey', () => { + test('uses custom validator when provided', () => { + const list = new VariableList({}, { + validateKey: (key) => { + if (key.startsWith('_')) throw new Error('No leading underscores'); + } + }); + expect(() => list.set('_bad', 'val')).toThrow('No leading underscores'); + expect(() => list.set('good', 'val')).not.toThrow(); + }); + }); +}); diff --git a/packages/bruno-tests/collection/scripting/api/bru/environment/folder.bru b/packages/bruno-tests/collection/scripting/api/bru/environment/folder.bru new file mode 100644 index 00000000000..aba41efad38 --- /dev/null +++ b/packages/bruno-tests/collection/scripting/api/bru/environment/folder.bru @@ -0,0 +1,8 @@ +meta { + name: environment + seq: 19 +} + +auth { + mode: inherit +} diff --git a/packages/bruno-tests/collection/scripting/api/bru/environment/get-set.bru b/packages/bruno-tests/collection/scripting/api/bru/environment/get-set.bru new file mode 100644 index 00000000000..03854254cd2 --- /dev/null +++ b/packages/bruno-tests/collection/scripting/api/bru/environment/get-set.bru @@ -0,0 +1,51 @@ +meta { + name: get-set + type: http + seq: 1 +} + +get { + url: {{host}}/ping + body: none + auth: inherit +} + +script:pre-request { + bru.environment.set("testEnvVar", "env-hello-123"); + bru.environment.set("testEnvObj", { api: "v2", debug: true }); +} + +tests { + test("get() should return the value set by set()", function() { + expect(bru.environment.get("testEnvVar")).to.equal("env-hello-123"); + }); + + test("get() should return object values", function() { + const obj = bru.environment.get("testEnvObj"); + expect(obj).to.have.property("api", "v2"); + expect(obj).to.have.property("debug", true); + }); + + test("get() should return existing env var (host)", function() { + const host = bru.environment.get("host"); + expect(host).to.be.a("string"); + expect(host).to.contain("http"); + }); + + test("get() should return undefined for nonexistent key", function() { + expect(bru.environment.get("nonexistent")).to.be.undefined; + }); + + test("set() should overwrite existing value", function() { + bru.environment.set("testEnvVar", "updated-456"); + expect(bru.environment.get("testEnvVar")).to.equal("updated-456"); + }); + + test("name should return the active environment name", function() { + const name = bru.environment.name; + expect(name).to.be.a("string"); + }); + + bru.environment.unset("testEnvVar"); + bru.environment.unset("testEnvObj"); +} diff --git a/packages/bruno-tests/collection/scripting/api/bru/environment/has-unset-clear.bru b/packages/bruno-tests/collection/scripting/api/bru/environment/has-unset-clear.bru new file mode 100644 index 00000000000..248c52d3bb3 --- /dev/null +++ b/packages/bruno-tests/collection/scripting/api/bru/environment/has-unset-clear.bru @@ -0,0 +1,51 @@ +meta { + name: has-unset-clear + type: http + seq: 2 +} + +get { + url: {{host}}/ping + body: none + auth: inherit +} + +script:pre-request { + // Save current env vars so we can restore after clear() + const saved = bru.environment.toObject(); + bru.setVar("__savedEnv", JSON.stringify(saved)); + + bru.environment.set("envAlpha", "1"); + bru.environment.set("envBeta", "2"); +} + +tests { + test("has() should return true for existing key", function() { + expect(bru.environment.has("envAlpha")).to.be.true; + }); + + test("has() should return false for nonexistent key", function() { + expect(bru.environment.has("nonexistent")).to.be.false; + }); + + test("unset() should remove a variable", function() { + bru.environment.unset("envAlpha"); + expect(bru.environment.has("envAlpha")).to.be.false; + }); + + test("clear() should remove all variables except __name__", function() { + bru.environment.clear(); + expect(bru.environment.has("envBeta")).to.be.false; + // name should still be accessible after clear + const name = bru.environment.name; + expect(name).to.be.a("string"); + }); +} + +script:post-response { + // Restore environment variables that were wiped by clear() + const saved = JSON.parse(bru.getVar("__savedEnv")); + for (const [key, value] of Object.entries(saved)) { + bru.environment.set(key, value); + } +} diff --git a/packages/bruno-tests/collection/scripting/api/bru/globals/folder.bru b/packages/bruno-tests/collection/scripting/api/bru/globals/folder.bru new file mode 100644 index 00000000000..cb098d12e4b --- /dev/null +++ b/packages/bruno-tests/collection/scripting/api/bru/globals/folder.bru @@ -0,0 +1,8 @@ +meta { + name: globals + seq: 20 +} + +auth { + mode: inherit +} diff --git a/packages/bruno-tests/collection/scripting/api/bru/globals/get-set.bru b/packages/bruno-tests/collection/scripting/api/bru/globals/get-set.bru new file mode 100644 index 00000000000..8181e55311f --- /dev/null +++ b/packages/bruno-tests/collection/scripting/api/bru/globals/get-set.bru @@ -0,0 +1,37 @@ +meta { + name: get-set + type: http + seq: 1 +} + +get { + url: {{host}}/ping + body: none + auth: inherit +} + +script:pre-request { + bru.globals.set("testGlobalVar", "global-hello-123"); + bru.globals.set("testGlobalObj", { scope: "global", active: true }); +} + +tests { + test("get() should return the value set by set()", function() { + expect(bru.globals.get("testGlobalVar")).to.equal("global-hello-123"); + }); + + test("get() should return object values", function() { + const obj = bru.globals.get("testGlobalObj"); + expect(obj).to.have.property("scope", "global"); + expect(obj).to.have.property("active", true); + }); + + test("get() should return undefined for nonexistent key", function() { + expect(bru.globals.get("nonexistent")).to.be.undefined; + }); + + test("set() should overwrite existing value", function() { + bru.globals.set("testGlobalVar", "updated-456"); + expect(bru.globals.get("testGlobalVar")).to.equal("updated-456"); + }); +} diff --git a/packages/bruno-tests/collection/scripting/api/bru/globals/has.bru b/packages/bruno-tests/collection/scripting/api/bru/globals/has.bru new file mode 100644 index 00000000000..085e2233ed1 --- /dev/null +++ b/packages/bruno-tests/collection/scripting/api/bru/globals/has.bru @@ -0,0 +1,33 @@ +meta { + name: has + type: http + seq: 2 +} + +get { + url: {{host}}/ping + body: none + auth: inherit +} + +script:pre-request { + bru.globals.set("globalAlpha", "1"); +} + +tests { + test("has() should return true for existing key", function() { + expect(bru.globals.has("globalAlpha")).to.be.true; + }); + + test("has() should return false for nonexistent key", function() { + expect(bru.globals.has("nonexistent")).to.be.false; + }); + + test("unset() should throw not implemented error", function() { + expect(function() { bru.globals.unset("globalAlpha"); }).to.throw("not implemented"); + }); + + test("clear() should throw not implemented error", function() { + expect(function() { bru.globals.clear(); }).to.throw("not implemented"); + }); +} diff --git a/packages/bruno-tests/collection/scripting/api/bru/variables/folder.bru b/packages/bruno-tests/collection/scripting/api/bru/variables/folder.bru new file mode 100644 index 00000000000..5e83f93df36 --- /dev/null +++ b/packages/bruno-tests/collection/scripting/api/bru/variables/folder.bru @@ -0,0 +1,8 @@ +meta { + name: variables + seq: 18 +} + +auth { + mode: inherit +} diff --git a/packages/bruno-tests/collection/scripting/api/bru/variables/get-set.bru b/packages/bruno-tests/collection/scripting/api/bru/variables/get-set.bru new file mode 100644 index 00000000000..901b8b56c21 --- /dev/null +++ b/packages/bruno-tests/collection/scripting/api/bru/variables/get-set.bru @@ -0,0 +1,37 @@ +meta { + name: get-set + type: http + seq: 1 +} + +get { + url: {{host}}/ping + body: none + auth: inherit +} + +script:pre-request { + bru.variables.set("testVar", "hello-123"); + bru.variables.set("objVar", { name: "bruno", version: 2 }); +} + +tests { + test("get() should return the value set by set()", function() { + expect(bru.variables.get("testVar")).to.equal("hello-123"); + }); + + test("get() should return object values", function() { + const obj = bru.variables.get("objVar"); + expect(obj).to.have.property("name", "bruno"); + expect(obj).to.have.property("version", 2); + }); + + test("get() should return undefined for nonexistent key", function() { + expect(bru.variables.get("nonexistent")).to.be.undefined; + }); + + test("set() should overwrite existing value", function() { + bru.variables.set("testVar", "updated-456"); + expect(bru.variables.get("testVar")).to.equal("updated-456"); + }); +} diff --git a/packages/bruno-tests/collection/scripting/api/bru/variables/has-unset-clear.bru b/packages/bruno-tests/collection/scripting/api/bru/variables/has-unset-clear.bru new file mode 100644 index 00000000000..80f832b0b52 --- /dev/null +++ b/packages/bruno-tests/collection/scripting/api/bru/variables/has-unset-clear.bru @@ -0,0 +1,39 @@ +meta { + name: has-unset-clear + type: http + seq: 2 +} + +get { + url: {{host}}/ping + body: none + auth: inherit +} + +script:pre-request { + bru.variables.set("alpha", "1"); + bru.variables.set("beta", "2"); + bru.variables.set("gamma", "3"); +} + +tests { + test("has() should return true for existing key", function() { + expect(bru.variables.has("alpha")).to.be.true; + }); + + test("has() should return false for nonexistent key", function() { + expect(bru.variables.has("nonexistent")).to.be.false; + }); + + test("unset() should remove a variable", function() { + bru.variables.unset("alpha"); + expect(bru.variables.has("alpha")).to.be.false; + }); + + test("clear() should remove all variables", function() { + bru.variables.clear(); + expect(bru.variables.has("beta")).to.be.false; + expect(bru.variables.has("gamma")).to.be.false; + expect(Object.keys(bru.variables.toObject()).length).to.equal(0); + }); +} diff --git a/tests/scripting/bru-api/environment/environment.spec.ts b/tests/scripting/bru-api/environment/environment.spec.ts new file mode 100644 index 00000000000..ed075452d7c --- /dev/null +++ b/tests/scripting/bru-api/environment/environment.spec.ts @@ -0,0 +1,26 @@ +import { test } from '../../../../playwright'; +import { setSandboxMode, runFolder, selectEnvironment, validateRunnerResults } from '../../../utils/page'; + +test.describe.serial('bru.environment API', () => { + test('all environment tests pass in developer mode', async ({ pageWithUserData: page }) => { + await setSandboxMode(page, 'bruno-testbench', 'developer'); + await selectEnvironment(page, 'Local'); + await runFolder(page, 'bruno-testbench', ['scripting', 'api', 'bru', 'environment']); + await validateRunnerResults(page, { + totalRequests: 2, + passed: 2, + failed: 0 + }); + }); + + test('all environment tests pass in safe mode', async ({ pageWithUserData: page }) => { + await setSandboxMode(page, 'bruno-testbench', 'safe'); + await selectEnvironment(page, 'Local'); + await runFolder(page, 'bruno-testbench', ['scripting', 'api', 'bru', 'environment']); + await validateRunnerResults(page, { + totalRequests: 2, + passed: 2, + failed: 0 + }); + }); +}); diff --git a/tests/scripting/bru-api/environment/init-user-data/collection-security.json b/tests/scripting/bru-api/environment/init-user-data/collection-security.json new file mode 100644 index 00000000000..bf37743a700 --- /dev/null +++ b/tests/scripting/bru-api/environment/init-user-data/collection-security.json @@ -0,0 +1,10 @@ +{ + "collections": [ + { + "path": "{{projectRoot}}/packages/bruno-tests/collection", + "securityConfig": { + "jsSandboxMode": "developer" + } + } + ] +} diff --git a/tests/scripting/bru-api/environment/init-user-data/preferences.json b/tests/scripting/bru-api/environment/init-user-data/preferences.json new file mode 100644 index 00000000000..d6762015c52 --- /dev/null +++ b/tests/scripting/bru-api/environment/init-user-data/preferences.json @@ -0,0 +1,12 @@ +{ + "maximized": false, + "lastOpenedCollections": [ + "{{projectRoot}}/packages/bruno-tests/collection" + ], + "preferences": { + "onboarding": { + "hasLaunchedBefore": true, + "hasSeenWelcomeModal": true + } + } +} diff --git a/tests/scripting/bru-api/globals/globals.spec.ts b/tests/scripting/bru-api/globals/globals.spec.ts new file mode 100644 index 00000000000..1da44adc29e --- /dev/null +++ b/tests/scripting/bru-api/globals/globals.spec.ts @@ -0,0 +1,26 @@ +import { test } from '../../../../playwright'; +import { setSandboxMode, runFolder, selectEnvironment, validateRunnerResults } from '../../../utils/page'; + +test.describe.serial('bru.globals API', () => { + test('all globals tests pass in developer mode', async ({ pageWithUserData: page }) => { + await setSandboxMode(page, 'bruno-testbench', 'developer'); + await selectEnvironment(page, 'Local'); + await runFolder(page, 'bruno-testbench', ['scripting', 'api', 'bru', 'globals']); + await validateRunnerResults(page, { + totalRequests: 2, + passed: 2, + failed: 0 + }); + }); + + test('all globals tests pass in safe mode', async ({ pageWithUserData: page }) => { + await setSandboxMode(page, 'bruno-testbench', 'safe'); + await selectEnvironment(page, 'Local'); + await runFolder(page, 'bruno-testbench', ['scripting', 'api', 'bru', 'globals']); + await validateRunnerResults(page, { + totalRequests: 2, + passed: 2, + failed: 0 + }); + }); +}); diff --git a/tests/scripting/bru-api/globals/init-user-data/collection-security.json b/tests/scripting/bru-api/globals/init-user-data/collection-security.json new file mode 100644 index 00000000000..bf37743a700 --- /dev/null +++ b/tests/scripting/bru-api/globals/init-user-data/collection-security.json @@ -0,0 +1,10 @@ +{ + "collections": [ + { + "path": "{{projectRoot}}/packages/bruno-tests/collection", + "securityConfig": { + "jsSandboxMode": "developer" + } + } + ] +} diff --git a/tests/scripting/bru-api/globals/init-user-data/preferences.json b/tests/scripting/bru-api/globals/init-user-data/preferences.json new file mode 100644 index 00000000000..d6762015c52 --- /dev/null +++ b/tests/scripting/bru-api/globals/init-user-data/preferences.json @@ -0,0 +1,12 @@ +{ + "maximized": false, + "lastOpenedCollections": [ + "{{projectRoot}}/packages/bruno-tests/collection" + ], + "preferences": { + "onboarding": { + "hasLaunchedBefore": true, + "hasSeenWelcomeModal": true + } + } +} diff --git a/tests/scripting/bru-api/variables/init-user-data/collection-security.json b/tests/scripting/bru-api/variables/init-user-data/collection-security.json new file mode 100644 index 00000000000..bf37743a700 --- /dev/null +++ b/tests/scripting/bru-api/variables/init-user-data/collection-security.json @@ -0,0 +1,10 @@ +{ + "collections": [ + { + "path": "{{projectRoot}}/packages/bruno-tests/collection", + "securityConfig": { + "jsSandboxMode": "developer" + } + } + ] +} diff --git a/tests/scripting/bru-api/variables/init-user-data/preferences.json b/tests/scripting/bru-api/variables/init-user-data/preferences.json new file mode 100644 index 00000000000..d6762015c52 --- /dev/null +++ b/tests/scripting/bru-api/variables/init-user-data/preferences.json @@ -0,0 +1,12 @@ +{ + "maximized": false, + "lastOpenedCollections": [ + "{{projectRoot}}/packages/bruno-tests/collection" + ], + "preferences": { + "onboarding": { + "hasLaunchedBefore": true, + "hasSeenWelcomeModal": true + } + } +} diff --git a/tests/scripting/bru-api/variables/variables.spec.ts b/tests/scripting/bru-api/variables/variables.spec.ts new file mode 100644 index 00000000000..ef738791c4a --- /dev/null +++ b/tests/scripting/bru-api/variables/variables.spec.ts @@ -0,0 +1,26 @@ +import { test } from '../../../../playwright'; +import { setSandboxMode, runFolder, selectEnvironment, validateRunnerResults } from '../../../utils/page'; + +test.describe.serial('bru.variables API', () => { + test('all variables tests pass in developer mode', async ({ pageWithUserData: page }) => { + await setSandboxMode(page, 'bruno-testbench', 'developer'); + await selectEnvironment(page, 'Local'); + await runFolder(page, 'bruno-testbench', ['scripting', 'api', 'bru', 'variables']); + await validateRunnerResults(page, { + totalRequests: 2, + passed: 2, + failed: 0 + }); + }); + + test('all variables tests pass in safe mode', async ({ pageWithUserData: page }) => { + await setSandboxMode(page, 'bruno-testbench', 'safe'); + await selectEnvironment(page, 'Local'); + await runFolder(page, 'bruno-testbench', ['scripting', 'api', 'bru', 'variables']); + await validateRunnerResults(page, { + totalRequests: 2, + passed: 2, + failed: 0 + }); + }); +});