mirror of
https://github.com/usebruno/bruno.git
synced 2026-06-24 21:25:45 +00:00
feat: add options to skip request and response bodies in reporter output (#7114)
* feat: add options to skip request and response bodies in reporter output - Introduced `--reporter-skip-request-body` and `--reporter-skip-response-body` flags to omit respective bodies from the reporter output. - Updated examples in the CLI documentation to reflect new options. - Refactored result sanitization to handle new flags. * feat: add shorthand option to skip both request and response bodies in reporter output - Introduced `--reporter-skip-body` as a shorthand for omitting both request and response bodies from the reporter output. - Updated CLI documentation examples to include the new shorthand option. - Adjusted result sanitization to accommodate the new option. * refactor: simplify documentation and tests for reporter-skip-body option - Updated the description of the `--reporter-skip-body` option to remove redundancy. - Removed outdated shorthand references from the test suite for clarity. - Cleaned up examples in the CLI documentation to focus on the current functionality. * fix: handle optional chaining for request and response properties in result sanitization - Updated the `sanitizeResultsForReporter` function to use optional chaining when accessing request and response headers and data. - This change prevents potential errors when these properties are undefined. * test: enhance reporter-skip-body tests for JSON and HTML outputs - Added comprehensive tests for the `--reporter-skip-request-body` and `--reporter-skip-response-body` options in both JSON and HTML report formats. - Verified that the appropriate request and response bodies are included or excluded based on the specified flags. - Improved test coverage for scenarios where both flags are used simultaneously. * fix: remove optional chaining for request and response headers in result sanitization - Updated the `sanitizeResultsForReporter` function to directly assign empty objects to request and response headers, ensuring consistent behavior regardless of their initial state. - This change simplifies the code and maintains functionality for skipping headers.
This commit is contained in:
@@ -18,6 +18,7 @@ const constants = require('../constants');
|
||||
const { findItemInCollection, createCollectionJsonFromPathname, getCallStack, FORMAT_CONFIG } = require('../utils/collection');
|
||||
const { hasExecutableTestInScript } = require('../utils/request');
|
||||
const { createSkippedFileResults } = require('../utils/run');
|
||||
const { sanitizeResultsForReporter } = require('../utils/sanitize-results');
|
||||
const { getSystemProxy } = require('@usebruno/requests');
|
||||
const command = 'run [paths...]';
|
||||
const desc = 'Run one or more requests/folders';
|
||||
@@ -200,6 +201,21 @@ const builder = async (yargs) => {
|
||||
description: 'Skip specific headers from the reporter output',
|
||||
default: []
|
||||
})
|
||||
.option('reporter-skip-request-body', {
|
||||
type: 'boolean',
|
||||
description: 'Omit request body from the reporter output',
|
||||
default: false
|
||||
})
|
||||
.option('reporter-skip-response-body', {
|
||||
type: 'boolean',
|
||||
description: 'Omit response body from the reporter output',
|
||||
default: false
|
||||
})
|
||||
.option('reporter-skip-body', {
|
||||
type: 'boolean',
|
||||
description: 'Omit both request and response bodies from the reporter output',
|
||||
default: false
|
||||
})
|
||||
.option('client-cert-config', {
|
||||
type: 'string',
|
||||
description: 'Path to the Client certificate config file used for securing the connection in the request'
|
||||
@@ -232,6 +248,9 @@ const builder = async (yargs) => {
|
||||
.example('$0 run folder -r', 'Run all requests in a folder recursively')
|
||||
.example('$0 run request.bru folder', 'Run a request and all requests in a folder')
|
||||
.example('$0 run --reporter-skip-all-headers', 'Run all requests in a folder recursively with omitted headers from the reporter output')
|
||||
.example('$0 run --reporter-skip-request-body', 'Run all requests with request bodies omitted from the reporter output')
|
||||
.example('$0 run --reporter-skip-response-body', 'Run all requests with response bodies omitted from the reporter output')
|
||||
.example('$0 run --reporter-skip-body', 'Run all requests with both request and response bodies omitted from the reporter output')
|
||||
.example(
|
||||
'$0 run --reporter-skip-headers "Authorization"',
|
||||
'Run all requests in a folder recursively with skipped headers from the reporter output'
|
||||
@@ -306,6 +325,9 @@ const handler = async function (argv) {
|
||||
bail,
|
||||
reporterSkipAllHeaders,
|
||||
reporterSkipHeaders,
|
||||
reporterSkipRequestBody,
|
||||
reporterSkipResponseBody,
|
||||
reporterSkipBody,
|
||||
clientCertConfig,
|
||||
noproxy,
|
||||
delay,
|
||||
@@ -686,35 +708,12 @@ const handler = async function (argv) {
|
||||
path: result.test?.filename || path.relative(collectionPath, pathname)
|
||||
});
|
||||
|
||||
if (reporterSkipAllHeaders) {
|
||||
results.forEach((result) => {
|
||||
result.request.headers = {};
|
||||
result.response.headers = {};
|
||||
});
|
||||
}
|
||||
|
||||
const deleteHeaderIfExists = (headers, header) => {
|
||||
Object.keys(headers).forEach((key) => {
|
||||
if (key.toLowerCase() === header.toLowerCase()) {
|
||||
delete headers[key];
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
if (reporterSkipHeaders?.length) {
|
||||
results.forEach((result) => {
|
||||
if (result.request?.headers) {
|
||||
reporterSkipHeaders.forEach((header) => {
|
||||
deleteHeaderIfExists(result.request.headers, header);
|
||||
});
|
||||
}
|
||||
if (result.response?.headers) {
|
||||
reporterSkipHeaders.forEach((header) => {
|
||||
deleteHeaderIfExists(result.response.headers, header);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
sanitizeResultsForReporter(results, {
|
||||
skipAllHeaders: reporterSkipAllHeaders,
|
||||
skipHeaders: reporterSkipHeaders,
|
||||
skipRequestBody: reporterSkipRequestBody || reporterSkipBody,
|
||||
skipResponseBody: reporterSkipResponseBody || reporterSkipBody
|
||||
});
|
||||
|
||||
// bail if option is set and there is a failure
|
||||
if (bail) {
|
||||
|
||||
45
packages/bruno-cli/src/utils/sanitize-results.js
Normal file
45
packages/bruno-cli/src/utils/sanitize-results.js
Normal file
@@ -0,0 +1,45 @@
|
||||
const deleteHeaderIfExists = (headers, header) => {
|
||||
Object.keys(headers).forEach((key) => {
|
||||
if (key.toLowerCase() === header.toLowerCase()) {
|
||||
delete headers[key];
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const sanitizeResultsForReporter = (results, { skipAllHeaders = false, skipHeaders = [], skipRequestBody = false, skipResponseBody = false } = {}) => {
|
||||
if (skipAllHeaders) {
|
||||
results.forEach((result) => {
|
||||
result.request.headers = {};
|
||||
result.response.headers = {};
|
||||
});
|
||||
}
|
||||
|
||||
if (skipHeaders?.length) {
|
||||
results.forEach((result) => {
|
||||
if (result.request?.headers) {
|
||||
skipHeaders.forEach((header) => {
|
||||
deleteHeaderIfExists(result.request.headers, header);
|
||||
});
|
||||
}
|
||||
if (result.response?.headers) {
|
||||
skipHeaders.forEach((header) => {
|
||||
deleteHeaderIfExists(result.response.headers, header);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (skipRequestBody) {
|
||||
results.forEach((result) => {
|
||||
delete result.request?.data;
|
||||
});
|
||||
}
|
||||
|
||||
if (skipResponseBody) {
|
||||
results.forEach((result) => {
|
||||
delete result.response?.data;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = { sanitizeResultsForReporter };
|
||||
184
packages/bruno-cli/tests/reporters/skip-body.spec.js
Normal file
184
packages/bruno-cli/tests/reporters/skip-body.spec.js
Normal file
@@ -0,0 +1,184 @@
|
||||
const { describe, it, expect } = require('@jest/globals');
|
||||
const { generateHtmlReport } = require('@usebruno/common/runner');
|
||||
|
||||
const { sanitizeResultsForReporter } = require('../../src/utils/sanitize-results');
|
||||
|
||||
const REQUEST_DATA = { username: 'john', password: 'secret123' };
|
||||
const RESPONSE_DATA = { id: 1, username: 'john', email: 'john@example.com' };
|
||||
|
||||
const createMockResult = () => ({
|
||||
test: { filename: 'echo/echo-post.bru' },
|
||||
request: {
|
||||
method: 'POST',
|
||||
url: 'https://echo.usebruno.com',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
data: { ...REQUEST_DATA }
|
||||
},
|
||||
response: {
|
||||
status: 200,
|
||||
statusText: 'OK',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
data: { ...RESPONSE_DATA },
|
||||
url: 'https://echo.usebruno.com',
|
||||
responseTime: 150
|
||||
},
|
||||
error: null,
|
||||
status: 'pass',
|
||||
assertionResults: [
|
||||
{ lhsExpr: 'res.status', rhsExpr: 'eq 200', status: 'pass' }
|
||||
],
|
||||
testResults: [
|
||||
{ description: 'should return user data', status: 'pass' }
|
||||
],
|
||||
preRequestTestResults: [],
|
||||
postResponseTestResults: [],
|
||||
name: 'echo post',
|
||||
path: 'echo/echo-post.bru',
|
||||
runDuration: 0.150
|
||||
});
|
||||
|
||||
describe('reporter-skip-body', () => {
|
||||
describe('JSON report', () => {
|
||||
it('should exclude both request and response bodies with --reporter-skip-body', () => {
|
||||
const results = [createMockResult()];
|
||||
// --reporter-skip-body sets both skipRequestBody and skipResponseBody to true
|
||||
sanitizeResultsForReporter(results, { skipRequestBody: true, skipResponseBody: true });
|
||||
const json = JSON.parse(JSON.stringify({ summary: {}, results }));
|
||||
|
||||
expect(json.results[0].request).not.toHaveProperty('data');
|
||||
expect(json.results[0].response).not.toHaveProperty('data');
|
||||
});
|
||||
});
|
||||
|
||||
describe('HTML report', () => {
|
||||
const extractEmbeddedData = (htmlString) => {
|
||||
const match = htmlString.match(/JSON\.parse\(decodeBase64\('([^']+)'\)\)/);
|
||||
expect(match).not.toBeNull();
|
||||
const binary = atob(match[1]);
|
||||
const bytes = Uint8Array.from(binary, (c) => c.charCodeAt(0));
|
||||
return JSON.parse(new TextDecoder().decode(bytes));
|
||||
};
|
||||
|
||||
const generateHtml = (results) => generateHtmlReport({
|
||||
runnerResults: [{
|
||||
iterationIndex: 0,
|
||||
results,
|
||||
summary: { totalRequests: 1, passedRequests: 1, failedRequests: 0, errorRequests: 0, skippedRequests: 0, totalAssertions: 1, passedAssertions: 1, failedAssertions: 0, totalTests: 1, passedTests: 1, failedTests: 0 }
|
||||
}],
|
||||
version: 'usebruno v1.16.0',
|
||||
environment: null,
|
||||
runCompletionTime: '2024-01-15T14:30:45.123Z'
|
||||
});
|
||||
|
||||
it('should exclude both bodies from HTML report with --reporter-skip-body', () => {
|
||||
const results = [createMockResult()];
|
||||
sanitizeResultsForReporter(results, { skipRequestBody: true, skipResponseBody: true });
|
||||
const embedded = extractEmbeddedData(generateHtml(results));
|
||||
const result = embedded.results[0].results[0];
|
||||
|
||||
expect(result.request).not.toHaveProperty('data');
|
||||
expect(result.response).not.toHaveProperty('data');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('reporter-skip-request-body and reporter-skip-response-body', () => {
|
||||
// --- JSON Report ---
|
||||
describe('JSON report', () => {
|
||||
it('should include both bodies by default', () => {
|
||||
const results = [createMockResult()];
|
||||
const json = JSON.parse(JSON.stringify({ summary: {}, results }));
|
||||
|
||||
expect(json.results[0].request.data).toEqual(REQUEST_DATA);
|
||||
expect(json.results[0].response.data).toEqual(RESPONSE_DATA);
|
||||
});
|
||||
|
||||
it('should exclude only request body with --reporter-skip-request-body', () => {
|
||||
const results = [createMockResult()];
|
||||
sanitizeResultsForReporter(results, { skipRequestBody: true });
|
||||
const json = JSON.parse(JSON.stringify({ summary: {}, results }));
|
||||
|
||||
expect(json.results[0].request).not.toHaveProperty('data');
|
||||
expect(json.results[0].response.data).toEqual(RESPONSE_DATA);
|
||||
});
|
||||
|
||||
it('should exclude only response body with --reporter-skip-response-body', () => {
|
||||
const results = [createMockResult()];
|
||||
sanitizeResultsForReporter(results, { skipResponseBody: true });
|
||||
const json = JSON.parse(JSON.stringify({ summary: {}, results }));
|
||||
|
||||
expect(json.results[0].request.data).toEqual(REQUEST_DATA);
|
||||
expect(json.results[0].response).not.toHaveProperty('data');
|
||||
});
|
||||
|
||||
it('should exclude both bodies when both flags are used', () => {
|
||||
const results = [createMockResult()];
|
||||
sanitizeResultsForReporter(results, { skipRequestBody: true, skipResponseBody: true });
|
||||
const json = JSON.parse(JSON.stringify({ summary: {}, results }));
|
||||
|
||||
expect(json.results[0].request).not.toHaveProperty('data');
|
||||
expect(json.results[0].response).not.toHaveProperty('data');
|
||||
});
|
||||
});
|
||||
|
||||
// --- HTML Report ---
|
||||
describe('HTML report', () => {
|
||||
const extractEmbeddedData = (htmlString) => {
|
||||
const match = htmlString.match(/JSON\.parse\(decodeBase64\('([^']+)'\)\)/);
|
||||
expect(match).not.toBeNull();
|
||||
const binary = atob(match[1]);
|
||||
const bytes = Uint8Array.from(binary, (c) => c.charCodeAt(0));
|
||||
return JSON.parse(new TextDecoder().decode(bytes));
|
||||
};
|
||||
|
||||
const generateHtml = (results) => generateHtmlReport({
|
||||
runnerResults: [{
|
||||
iterationIndex: 0,
|
||||
results,
|
||||
summary: { totalRequests: 1, passedRequests: 1, failedRequests: 0, errorRequests: 0, skippedRequests: 0, totalAssertions: 1, passedAssertions: 1, failedAssertions: 0, totalTests: 1, passedTests: 1, failedTests: 0 }
|
||||
}],
|
||||
version: 'usebruno v1.16.0',
|
||||
environment: null,
|
||||
runCompletionTime: '2024-01-15T14:30:45.123Z'
|
||||
});
|
||||
|
||||
it('should include both bodies by default', () => {
|
||||
const results = [createMockResult()];
|
||||
const embedded = extractEmbeddedData(generateHtml(results));
|
||||
const result = embedded.results[0].results[0];
|
||||
|
||||
expect(result.request).toHaveProperty('data');
|
||||
expect(result.response).toHaveProperty('data');
|
||||
});
|
||||
|
||||
it('should exclude only request body with --reporter-skip-request-body', () => {
|
||||
const results = [createMockResult()];
|
||||
sanitizeResultsForReporter(results, { skipRequestBody: true });
|
||||
const embedded = extractEmbeddedData(generateHtml(results));
|
||||
const result = embedded.results[0].results[0];
|
||||
|
||||
expect(result.request).not.toHaveProperty('data');
|
||||
expect(result.response).toHaveProperty('data');
|
||||
});
|
||||
|
||||
it('should exclude only response body with --reporter-skip-response-body', () => {
|
||||
const results = [createMockResult()];
|
||||
sanitizeResultsForReporter(results, { skipResponseBody: true });
|
||||
const embedded = extractEmbeddedData(generateHtml(results));
|
||||
const result = embedded.results[0].results[0];
|
||||
|
||||
expect(result.request).toHaveProperty('data');
|
||||
expect(result.response).not.toHaveProperty('data');
|
||||
});
|
||||
|
||||
it('should exclude both bodies when both flags are used', () => {
|
||||
const results = [createMockResult()];
|
||||
sanitizeResultsForReporter(results, { skipRequestBody: true, skipResponseBody: true });
|
||||
const embedded = extractEmbeddedData(generateHtml(results));
|
||||
const result = embedded.results[0].results[0];
|
||||
|
||||
expect(result.request).not.toHaveProperty('data');
|
||||
expect(result.response).not.toHaveProperty('data');
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user