mirror of
https://github.com/usebruno/bruno.git
synced 2026-06-28 07:04:10 +00:00
Feat: Support multipart/mixed (#7155)
* feat(): support multipart mixed fix: support vars interpolation on mixed multi-part Update packages/bruno-electron/src/ipc/network/interpolate-vars.js Co-authored-by: Timon <39559178+Its-treason@users.noreply.github.com> refactor: use startsWith feat: best effort for other multipart/* contentypes * feat: enhance variable interpolation for multipart requests - Updated `interpolateVars` function to support interpolation in multipart/form-data and multipart/mixed requests. - Added handling for empty multipart arrays and parts with missing or undefined values. - Improved type checks for content types to ensure proper interpolation behavior. Includes new tests to validate the interpolation functionality for multipart requests. * fix: normalize error handling in sendRequest and improve test reliability --------- Co-authored-by: Alfonso Presa <alfonso-presa@users.noreply.github.com>
This commit is contained in:
@@ -294,4 +294,136 @@ describe('interpolate-vars: interpolateVars', () => {
|
||||
expect(result.data).toEqual(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Multipart body (multipart/form-data and multipart/mixed)', () => {
|
||||
it('interpolates value in each part when Content-Type is multipart/form-data', () => {
|
||||
const request = {
|
||||
method: 'POST',
|
||||
url: 'http://api.example/upload',
|
||||
headers: { 'Content-Type': 'multipart/form-data; boundary=----boundary' },
|
||||
data: [
|
||||
{ name: 'field1', value: '{{token}}', type: 'text' },
|
||||
{ name: 'field2', value: 'static', type: 'text' },
|
||||
{ name: 'field3', value: '{{prefix}}-suffix', type: 'text' }
|
||||
]
|
||||
};
|
||||
|
||||
const result = interpolateVars(
|
||||
request,
|
||||
{ token: 'secret123', prefix: 'my' },
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
expect(result.data).toEqual([
|
||||
{ name: 'field1', value: 'secret123', type: 'text' },
|
||||
{ name: 'field2', value: 'static', type: 'text' },
|
||||
{ name: 'field3', value: 'my-suffix', type: 'text' }
|
||||
]);
|
||||
});
|
||||
|
||||
it('interpolates value in each part when Content-Type is multipart/mixed', () => {
|
||||
const request = {
|
||||
method: 'POST',
|
||||
url: 'http://api.example/send',
|
||||
headers: { 'Content-Type': 'multipart/mixed; boundary=----mixed' },
|
||||
data: [
|
||||
{ name: 'part1', value: '{{envVar}}', type: 'text' },
|
||||
{ name: 'part2', value: '{{another}}', type: 'text' }
|
||||
]
|
||||
};
|
||||
|
||||
const result = interpolateVars(
|
||||
request,
|
||||
{ envVar: 'first', another: 'second' },
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
expect(result.data).toEqual([
|
||||
{ name: 'part1', value: 'first', type: 'text' },
|
||||
{ name: 'part2', value: 'second', type: 'text' }
|
||||
]);
|
||||
});
|
||||
|
||||
it('leaves part keys (name, type, etc.) intact and only interpolates value', () => {
|
||||
const request = {
|
||||
method: 'POST',
|
||||
url: 'http://api.example/upload',
|
||||
headers: { 'Content-Type': 'multipart/form-data' },
|
||||
data: [
|
||||
{ name: 'file', value: '{{path}}', type: 'file', fileName: 'doc.pdf' }
|
||||
]
|
||||
};
|
||||
|
||||
const result = interpolateVars(request, { path: '/tmp/doc.pdf' }, null, null);
|
||||
|
||||
expect(result.data).toHaveLength(1);
|
||||
expect(result.data[0].name).toBe('file');
|
||||
expect(result.data[0].type).toBe('file');
|
||||
expect(result.data[0].fileName).toBe('doc.pdf');
|
||||
expect(result.data[0].value).toBe('/tmp/doc.pdf');
|
||||
});
|
||||
|
||||
it('handles empty multipart array', () => {
|
||||
const request = {
|
||||
method: 'POST',
|
||||
url: 'http://api.example/upload',
|
||||
headers: { 'Content-Type': 'multipart/form-data' },
|
||||
data: []
|
||||
};
|
||||
|
||||
const result = interpolateVars(request, { x: 'y' }, null, null);
|
||||
|
||||
expect(result.data).toEqual([]);
|
||||
});
|
||||
|
||||
it('handles part with missing or undefined value', () => {
|
||||
const request = {
|
||||
method: 'POST',
|
||||
url: 'http://api.example/upload',
|
||||
headers: { 'Content-Type': 'multipart/form-data' },
|
||||
data: [
|
||||
{ name: 'a', value: '{{present}}' },
|
||||
{ name: 'b' },
|
||||
{ name: 'c', value: undefined }
|
||||
]
|
||||
};
|
||||
|
||||
const result = interpolateVars(request, { present: 'ok' }, null, null);
|
||||
|
||||
expect(result.data[0].value).toBe('ok');
|
||||
expect(result.data[1].value).toBeUndefined();
|
||||
expect(result.data[2].value).toBeUndefined();
|
||||
});
|
||||
|
||||
it('preserves raw string body when Content-Type is multipart/mixed (manually constructed multipart)', () => {
|
||||
// Equivalent to: curl -X POST https://httpbin.dev/post \
|
||||
// -H 'content-type: multipart/mixed; boundary=TestBoundary123' \
|
||||
// --data '--TestBoundary123\r\nContent-Type: application/json\r\n\r\n{"test": true}\r\n--TestBoundary123--\r\n'
|
||||
const rawMultipartBody = [
|
||||
'--TestBoundary123',
|
||||
'Content-Type: application/json',
|
||||
'',
|
||||
'{"test": true}',
|
||||
'--TestBoundary123--',
|
||||
''
|
||||
].join('\r\n');
|
||||
|
||||
const request = {
|
||||
method: 'POST',
|
||||
url: 'https://httpbin.dev/post',
|
||||
headers: { 'content-type': 'multipart/mixed; boundary=TestBoundary123' },
|
||||
data: rawMultipartBody
|
||||
};
|
||||
|
||||
const result = interpolateVars(request, {}, null, null);
|
||||
|
||||
expect(result.data).toBe(rawMultipartBody);
|
||||
expect(result.data).toContain('--TestBoundary123');
|
||||
expect(result.data).toContain('Content-Type: application/json');
|
||||
expect(result.data).toContain('{"test": true}');
|
||||
expect(result.data).toContain('--TestBoundary123--');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user