mirror of
https://github.com/usebruno/bruno.git
synced 2026-06-27 14:44:07 +00:00
refactor: remove idx from HeaderList, extend ReadOnlyPropertyList, add positional method translations (#7917)
* refactor: remove 'idx' method from headerList and update related tests - Eliminated the 'idx' method from both req.headerList and res.headerList to streamline header management. - Updated associated tests and documentation to reflect the removal, ensuring clarity in the API usage and maintaining consistency across the header management system. * refactor: block unimplemented HeaderList methods with error messages - Added error handling for unimplemented methods in HeaderList, including idx, add, upsert, remove, each, prepend, insert, and insertAfter. - Each method now throws a descriptive error indicating the appropriate alternative methods to use, enhancing clarity in the API and guiding users towards correct usage. * refactor: update error message in idx() method of HeaderList for clarity - Modified the error message in the idx() method to guide users towards using all()[index] or get(name) instead of the unsupported idx() method, enhancing the clarity of the API documentation. * refactor: update HeaderList to extend ReadOnlyPropertyList and remove unimplemented methods - Changed HeaderList to extend ReadOnlyPropertyList instead of PropertyList, streamlining its functionality. - Removed unimplemented methods (prepend, insert, insertAfter) from HeaderList, clarifying the API and guiding users towards using supported methods. - Updated related tests to reflect these changes, ensuring consistency and accuracy in header management. * test: add translations for pm.request.headers methods in request tests - Introduced new tests to validate the translation of pm.request.headers methods (prepend, insert, insertAfter) to their corresponding req.headerList.append method. - Enhanced existing tests to ensure accurate conversion and functionality of header management in the Bruno converters. * feat: enhance header translation methods for pm.request.headers - Added translations for additional pm.request.headers methods (get, has, one, all, count, indexOf, find, filter, each, map, reduce, toObject, clear) to their corresponding req.headerList methods. - Updated tests to validate the new translations and ensure accurate header management functionality in the Bruno converters.
This commit is contained in:
@@ -43,7 +43,6 @@ const STATIC_API_HINTS = {
|
||||
'req.headerList.get(name)',
|
||||
'req.headerList.one(name)',
|
||||
'req.headerList.all()',
|
||||
'req.headerList.idx(index)',
|
||||
'req.headerList.count()',
|
||||
'req.headerList.has(name)',
|
||||
'req.headerList.has(name, value)',
|
||||
@@ -88,7 +87,6 @@ const STATIC_API_HINTS = {
|
||||
'res.headerList.get(name)',
|
||||
'res.headerList.one(name)',
|
||||
'res.headerList.all()',
|
||||
'res.headerList.idx(index)',
|
||||
'res.headerList.count()',
|
||||
'res.headerList.has(name)',
|
||||
'res.headerList.has(name, value)',
|
||||
|
||||
@@ -34,8 +34,48 @@ const replacements = {
|
||||
'pm\\.environment\\.toObject\\(': 'bru.getAllEnvVars(',
|
||||
'pm\\.environment\\.clear\\(': 'bru.deleteAllEnvVars(',
|
||||
'pm\\.variables\\.toObject\\(': 'bru.getAllVars(',
|
||||
// Request header PropertyList methods
|
||||
'pm\\.request\\.headers\\.remove\\(': 'req.deleteHeader(',
|
||||
'pm\\.request\\.headers\\.get\\(': 'req.headerList.get(',
|
||||
'pm\\.request\\.headers\\.has\\(': 'req.headerList.has(',
|
||||
'pm\\.request\\.headers\\.one\\(': 'req.headerList.one(',
|
||||
'pm\\.request\\.headers\\.all\\(': 'req.headerList.all(',
|
||||
'pm\\.request\\.headers\\.count\\(': 'req.headerList.count(',
|
||||
'pm\\.request\\.headers\\.indexOf\\(': 'req.headerList.indexOf(',
|
||||
'pm\\.request\\.headers\\.find\\(': 'req.headerList.find(',
|
||||
'pm\\.request\\.headers\\.filter\\(': 'req.headerList.filter(',
|
||||
'pm\\.request\\.headers\\.each\\(': 'req.headerList.forEach(',
|
||||
'pm\\.request\\.headers\\.map\\(': 'req.headerList.map(',
|
||||
'pm\\.request\\.headers\\.reduce\\(': 'req.headerList.reduce(',
|
||||
'pm\\.request\\.headers\\.toObject\\(': 'req.headerList.toObject(',
|
||||
'pm\\.request\\.headers\\.clear\\(': 'req.headerList.clear(',
|
||||
'pm\\.request\\.headers\\.add\\(': 'req.headerList.append(',
|
||||
'pm\\.request\\.headers\\.upsert\\(': 'req.headerList.set(',
|
||||
'pm\\.request\\.headers\\.toString\\(': 'req.headerList.toString(',
|
||||
'pm\\.request\\.headers\\.toJSON\\(': 'req.headerList.toJSON(',
|
||||
'pm\\.request\\.headers\\.populate\\(': 'req.headerList.populate(',
|
||||
'pm\\.request\\.headers\\.repopulate\\(': 'req.headerList.repopulate(',
|
||||
'pm\\.request\\.headers\\.assimilate\\(': 'req.headerList.assimilate(',
|
||||
// Lossy: positional inserts map to append (position irrelevant for headers)
|
||||
// Note: regex can't drop the second arg, so it passes through as-is
|
||||
'pm\\.request\\.headers\\.prepend\\(': 'req.headerList.append(',
|
||||
'pm\\.request\\.headers\\.insert\\(': 'req.headerList.append(',
|
||||
'pm\\.request\\.headers\\.insertAfter\\(': 'req.headerList.append(',
|
||||
// Response header PropertyList methods
|
||||
'pm\\.response\\.headers\\.get\\(': 'res.getHeader(',
|
||||
'pm\\.response\\.headers\\.has\\(': 'res.headerList.has(',
|
||||
'pm\\.response\\.headers\\.one\\(': 'res.headerList.one(',
|
||||
'pm\\.response\\.headers\\.all\\(': 'res.headerList.all(',
|
||||
'pm\\.response\\.headers\\.count\\(': 'res.headerList.count(',
|
||||
'pm\\.response\\.headers\\.indexOf\\(': 'res.headerList.indexOf(',
|
||||
'pm\\.response\\.headers\\.find\\(': 'res.headerList.find(',
|
||||
'pm\\.response\\.headers\\.filter\\(': 'res.headerList.filter(',
|
||||
'pm\\.response\\.headers\\.each\\(': 'res.headerList.forEach(',
|
||||
'pm\\.response\\.headers\\.map\\(': 'res.headerList.map(',
|
||||
'pm\\.response\\.headers\\.reduce\\(': 'res.headerList.reduce(',
|
||||
'pm\\.response\\.headers\\.toObject\\(': 'res.headerList.toObject(',
|
||||
'pm\\.response\\.headers\\.toString\\(': 'res.headerList.toString(',
|
||||
'pm\\.response\\.headers\\.toJSON\\(': 'res.headerList.toJSON(',
|
||||
'pm\\.response\\.to\\.have\\.jsonSchema\\(': 'expect(res.getBody()).to.have.jsonSchema(',
|
||||
'pm\\.response\\.to\\.not\\.have\\.jsonSchema\\(': 'expect(res.getBody()).to.not.have.jsonSchema(',
|
||||
'pm\\.response\\.not\\.to\\.have\\.jsonSchema\\(': 'expect(res.getBody()).not.to.have.jsonSchema(',
|
||||
|
||||
@@ -90,6 +90,8 @@ const simpleTranslations = {
|
||||
'req.headerList.map': 'pm.request.headers.map',
|
||||
'req.headerList.reduce': 'pm.request.headers.reduce',
|
||||
'req.headerList.toObject': 'pm.request.headers.toObject',
|
||||
'req.headerList.toString': 'pm.request.headers.toString',
|
||||
'req.headerList.toJSON': 'pm.request.headers.toJSON',
|
||||
'req.headerList.append': 'pm.request.headers.add',
|
||||
'req.headerList.set': 'pm.request.headers.upsert',
|
||||
'req.headerList.delete': 'pm.request.headers.remove',
|
||||
@@ -130,6 +132,8 @@ const simpleTranslations = {
|
||||
'res.headerList.map': 'pm.response.headers.map',
|
||||
'res.headerList.reduce': 'pm.response.headers.reduce',
|
||||
'res.headerList.toObject': 'pm.response.headers.toObject',
|
||||
'res.headerList.toString': 'pm.response.headers.toString',
|
||||
'res.headerList.toJSON': 'pm.response.headers.toJSON',
|
||||
|
||||
// Cookies jar
|
||||
'bru.cookies.jar': 'pm.cookies.jar',
|
||||
|
||||
@@ -65,6 +65,8 @@ const simpleTranslations = {
|
||||
'pm.request.headers.map': 'req.headerList.map',
|
||||
'pm.request.headers.reduce': 'req.headerList.reduce',
|
||||
'pm.request.headers.toObject': 'req.headerList.toObject',
|
||||
'pm.request.headers.toString': 'req.headerList.toString',
|
||||
'pm.request.headers.toJSON': 'req.headerList.toJSON',
|
||||
'pm.request.headers.clear': 'req.headerList.clear',
|
||||
|
||||
// Response headers PropertyList methods (read-only)
|
||||
@@ -79,6 +81,8 @@ const simpleTranslations = {
|
||||
'pm.response.headers.map': 'res.headerList.map',
|
||||
'pm.response.headers.reduce': 'res.headerList.reduce',
|
||||
'pm.response.headers.toObject': 'res.headerList.toObject',
|
||||
'pm.response.headers.toString': 'res.headerList.toString',
|
||||
'pm.response.headers.toJSON': 'res.headerList.toJSON',
|
||||
|
||||
// Request properties (pm.request.*)
|
||||
'pm.request.url.getHost': 'req.getHost',
|
||||
@@ -409,6 +413,32 @@ const complexTransformations = [
|
||||
}
|
||||
},
|
||||
|
||||
// Lossy: positional header inserts → append (only keep the first arg, drop positional ref)
|
||||
// pm.request.headers.prepend(item) -> req.headerList.append(item)
|
||||
{
|
||||
pattern: 'pm.request.headers.prepend',
|
||||
transform: (path, j) => {
|
||||
const args = path.parent.value.arguments;
|
||||
return j.callExpression(j.identifier('req.headerList.append'), args.length > 0 ? [args[0]] : []);
|
||||
}
|
||||
},
|
||||
// pm.request.headers.insert(item, before) -> req.headerList.append(item)
|
||||
{
|
||||
pattern: 'pm.request.headers.insert',
|
||||
transform: (path, j) => {
|
||||
const args = path.parent.value.arguments;
|
||||
return j.callExpression(j.identifier('req.headerList.append'), args.length > 0 ? [args[0]] : []);
|
||||
}
|
||||
},
|
||||
// pm.request.headers.insertAfter(item, after) -> req.headerList.append(item)
|
||||
{
|
||||
pattern: 'pm.request.headers.insertAfter',
|
||||
transform: (path, j) => {
|
||||
const args = path.parent.value.arguments;
|
||||
return j.callExpression(j.identifier('req.headerList.append'), args.length > 0 ? [args[0]] : []);
|
||||
}
|
||||
},
|
||||
|
||||
// pm.response.to.be.ok -> expect(res.getStatus()).to.be.within(200, 299)
|
||||
{
|
||||
pattern: 'pm.response.to.be.ok',
|
||||
|
||||
@@ -292,6 +292,18 @@ console.log("Headers:", JSON.stringify(pm.request.headers));
|
||||
expect(translatedCode).toBe('const obj = pm.request.headers.toObject();');
|
||||
});
|
||||
|
||||
it('should translate req.headerList.toString to pm.request.headers.toString', () => {
|
||||
const code = 'const str = req.headerList.toString();';
|
||||
const translatedCode = translateBruToPostman(code);
|
||||
expect(translatedCode).toBe('const str = pm.request.headers.toString();');
|
||||
});
|
||||
|
||||
it('should translate req.headerList.toJSON to pm.request.headers.toJSON', () => {
|
||||
const code = 'const json = req.headerList.toJSON();';
|
||||
const translatedCode = translateBruToPostman(code);
|
||||
expect(translatedCode).toBe('const json = pm.request.headers.toJSON();');
|
||||
});
|
||||
|
||||
it('should translate req.headerList.set to pm.request.headers.upsert', () => {
|
||||
const code = 'req.headerList.set({key: "X-Custom", value: "updated"});';
|
||||
const translatedCode = translateBruToPostman(code);
|
||||
|
||||
@@ -201,9 +201,39 @@ describe('Request Translation', () => {
|
||||
expect(translatedCode).toBe('req.headerList.clear();');
|
||||
});
|
||||
|
||||
it('should translate pm.request.headers.prepend to req.headerList.append', () => {
|
||||
const code = 'pm.request.headers.prepend({key: "X-First", value: "1"});';
|
||||
const translatedCode = translateCode(code);
|
||||
expect(translatedCode).toBe('req.headerList.append({key: "X-First", value: "1"});');
|
||||
});
|
||||
|
||||
it('should translate pm.request.headers.insert to req.headerList.append (drops positional ref)', () => {
|
||||
const code = 'pm.request.headers.insert({key: "X-Mid", value: "2"}, "ref");';
|
||||
const translatedCode = translateCode(code);
|
||||
expect(translatedCode).toBe('req.headerList.append({key: "X-Mid", value: "2"});');
|
||||
});
|
||||
|
||||
it('should translate pm.request.headers.insertAfter to req.headerList.append (drops positional ref)', () => {
|
||||
const code = 'pm.request.headers.insertAfter({key: "X-After", value: "3"}, "ref");';
|
||||
const translatedCode = translateCode(code);
|
||||
expect(translatedCode).toBe('req.headerList.append({key: "X-After", value: "3"});');
|
||||
});
|
||||
|
||||
it('should translate pm.request.headers.toObject to req.headerList.toObject', () => {
|
||||
const code = 'const obj = pm.request.headers.toObject();';
|
||||
const translatedCode = translateCode(code);
|
||||
expect(translatedCode).toBe('const obj = req.headerList.toObject();');
|
||||
});
|
||||
|
||||
it('should translate pm.request.headers.toString to req.headerList.toString', () => {
|
||||
const code = 'const str = pm.request.headers.toString();';
|
||||
const translatedCode = translateCode(code);
|
||||
expect(translatedCode).toBe('const str = req.headerList.toString();');
|
||||
});
|
||||
|
||||
it('should translate pm.request.headers.toJSON to req.headerList.toJSON', () => {
|
||||
const code = 'const json = pm.request.headers.toJSON();';
|
||||
const translatedCode = translateCode(code);
|
||||
expect(translatedCode).toBe('const json = req.headerList.toJSON();');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -894,4 +894,16 @@ describe('Response Translation', () => {
|
||||
const translatedCode = translateCode(code);
|
||||
expect(translatedCode).toBe('const obj = res.headerList.toObject();');
|
||||
});
|
||||
|
||||
it('should translate pm.response.headers.toString to res.headerList.toString', () => {
|
||||
const code = 'const str = pm.response.headers.toString();';
|
||||
const translatedCode = translateCode(code);
|
||||
expect(translatedCode).toBe('const str = res.headerList.toString();');
|
||||
});
|
||||
|
||||
it('should translate pm.response.headers.toJSON to res.headerList.toJSON', () => {
|
||||
const code = 'const json = pm.response.headers.toJSON();';
|
||||
const translatedCode = translateCode(code);
|
||||
expect(translatedCode).toBe('const json = res.headerList.toJSON();');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
const PropertyList = require('./property-list');
|
||||
const ReadOnlyPropertyList = require('./readonly-property-list');
|
||||
|
||||
/**
|
||||
@@ -78,7 +77,7 @@ const ReadOnlyPropertyList = require('./readonly-property-list');
|
||||
* | `repopulate(items)` | Clears all, then populates with new items |
|
||||
* | `assimilate(source, prune?)` | Merges headers; prune removes items not in source |
|
||||
*/
|
||||
class HeaderList extends PropertyList {
|
||||
class HeaderList extends ReadOnlyPropertyList {
|
||||
#req;
|
||||
#writable;
|
||||
|
||||
@@ -151,6 +150,12 @@ class HeaderList extends PropertyList {
|
||||
return { key: str.substring(0, idx).trim(), value: str.substring(idx + 1).trim() };
|
||||
}
|
||||
|
||||
// ── Blocked inherited methods ─────────────────────────────────────────
|
||||
// These are inherited from ReadOnlyPropertyList but are not part of
|
||||
// the HeaderList API. Set to undefined so they are not callable.
|
||||
idx = undefined;
|
||||
each = undefined;
|
||||
|
||||
// ── Read method overrides (case-insensitive) ──────────────────────────
|
||||
|
||||
/**
|
||||
|
||||
@@ -38,7 +38,7 @@ const addBrunoRequestShimToContext = (vm, req) => {
|
||||
const { evalCode: headersEvalCode } = createPropertyListBridge(vm, req.headerList, headerListObj, {
|
||||
globalPath: 'globalThis.req.headerList',
|
||||
syncReadMethods: ['get', 'has', 'count', 'indexOf', 'toObject', 'toString'],
|
||||
syncReadObjectMethods: ['one', 'all', 'idx', 'toJSON'],
|
||||
syncReadObjectMethods: ['one', 'all', 'toJSON'],
|
||||
syncWriteMethods: ['append', 'set', 'delete', 'clear', 'populate', 'repopulate', 'assimilate'],
|
||||
withIterators: true
|
||||
});
|
||||
@@ -197,7 +197,7 @@ const addBrunoRequestShimToContext = (vm, req) => {
|
||||
// Wrapped in a block to avoid const redeclaration conflicts with other evalCode blocks
|
||||
// The bridge generates `each` (shared with CookieList); alias `forEach` for HeaderList's MDN-style API
|
||||
if (headersEvalCode) {
|
||||
vm.evalCode(`{ ${headersEvalCode} globalThis.req.headerList.forEach = globalThis.req.headerList.each; }`);
|
||||
vm.evalCode(`{ ${headersEvalCode} globalThis.req.headerList.forEach = globalThis.req.headerList.each; delete globalThis.req.headerList.each; }`);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ const addBrunoResponseShimToContext = (vm, res) => {
|
||||
const bridge = createPropertyListBridge(vm, res.headerList, headerListObj, {
|
||||
globalPath: 'globalThis.res.headerList',
|
||||
syncReadMethods: ['get', 'has', 'count', 'indexOf', 'toObject', 'toString'],
|
||||
syncReadObjectMethods: ['one', 'all', 'idx', 'toJSON'],
|
||||
syncReadObjectMethods: ['one', 'all', 'toJSON'],
|
||||
syncWriteMethods: ['append', 'set', 'delete', 'clear', 'populate', 'repopulate', 'assimilate'],
|
||||
withIterators: true
|
||||
});
|
||||
@@ -133,7 +133,7 @@ const addBrunoResponseShimToContext = (vm, res) => {
|
||||
// Wrapped in a block to avoid const redeclaration conflicts with req.headerList's evalCode
|
||||
// The bridge generates `each` (shared with CookieList); alias `forEach` for HeaderList's MDN-style API
|
||||
if (resHeadersEvalCode) {
|
||||
vm.evalCode(`{ ${resHeadersEvalCode} globalThis.res.headerList.forEach = globalThis.res.headerList.each; }`);
|
||||
vm.evalCode(`{ ${resHeadersEvalCode} globalThis.res.headerList.forEach = globalThis.res.headerList.each; delete globalThis.res.headerList.each; }`);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
const HeaderList = require('../src/header-list');
|
||||
const PropertyList = require('../src/property-list');
|
||||
const ReadOnlyPropertyList = require('../src/readonly-property-list');
|
||||
const BrunoRequest = require('../src/bruno-request');
|
||||
const BrunoResponse = require('../src/bruno-response');
|
||||
@@ -19,10 +18,9 @@ describe('HeaderList (req.headerList)', () => {
|
||||
|
||||
// ── Inheritance ────────────────────────────────────────────────────────
|
||||
|
||||
test('extends PropertyList and ReadOnlyPropertyList', () => {
|
||||
test('extends ReadOnlyPropertyList', () => {
|
||||
const { list } = createReqHeaders();
|
||||
expect(list).toBeInstanceOf(ReadOnlyPropertyList);
|
||||
expect(list).toBeInstanceOf(PropertyList);
|
||||
expect(list).toBeInstanceOf(HeaderList);
|
||||
});
|
||||
|
||||
@@ -73,17 +71,6 @@ describe('HeaderList (req.headerList)', () => {
|
||||
expect(a1).not.toBe(a2);
|
||||
});
|
||||
|
||||
test('idx() returns header at position', () => {
|
||||
const { list } = createReqHeaders();
|
||||
expect(list.idx(0)).toEqual({ key: 'Content-Type', value: 'application/json' });
|
||||
expect(list.idx(2)).toEqual({ key: 'Accept', value: '*/*' });
|
||||
});
|
||||
|
||||
test('idx() returns undefined for out-of-bounds', () => {
|
||||
const { list } = createReqHeaders();
|
||||
expect(list.idx(10)).toBeUndefined();
|
||||
});
|
||||
|
||||
test('count() returns number of headers', () => {
|
||||
const { list } = createReqHeaders();
|
||||
expect(list.count()).toBe(3);
|
||||
@@ -923,11 +910,6 @@ describe('Response Headers (res.headerList)', () => {
|
||||
expect(headerList.count()).toBe(3);
|
||||
});
|
||||
|
||||
test('idx() returns header at position', () => {
|
||||
const { headerList } = createResHeaders();
|
||||
expect(headerList.idx(1)).toEqual({ key: 'x-request-id', value: 'abc-123' });
|
||||
});
|
||||
|
||||
test('indexOf() finds structurally-equal header', () => {
|
||||
const { headerList } = createResHeaders();
|
||||
expect(headerList.indexOf({ key: 'content-type', value: 'application/json' })).toBe(0);
|
||||
|
||||
@@ -44,13 +44,6 @@ tests {
|
||||
expect(keys).to.include('x-custom');
|
||||
});
|
||||
|
||||
test("req.headerList.idx(index)", function() {
|
||||
const first = req.headerList.idx(0);
|
||||
expect(first).to.have.property('key');
|
||||
expect(first).to.have.property('value');
|
||||
expect(req.headerList.idx(-1)).to.be.undefined;
|
||||
});
|
||||
|
||||
test("req.headerList.count()", function() {
|
||||
expect(req.headerList.count()).to.be.at.least(3);
|
||||
});
|
||||
|
||||
@@ -40,13 +40,6 @@ tests {
|
||||
expect(keys).to.include('x-powered-by');
|
||||
});
|
||||
|
||||
test("res.headerList.idx(index)", function() {
|
||||
const first = res.headerList.idx(0);
|
||||
expect(first).to.have.property('key');
|
||||
expect(first).to.have.property('value');
|
||||
expect(res.headerList.idx(-1)).to.be.undefined;
|
||||
});
|
||||
|
||||
test("res.headerList.count()", function() {
|
||||
expect(res.headerList.count()).to.be.at.least(1);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user