mirror of
https://github.com/usebruno/bruno.git
synced 2026-07-09 06:55:03 +00:00
Merge branch 'main' into main
This commit is contained in:
@@ -1,5 +1,9 @@
|
||||
# Changelog
|
||||
|
||||
## 0.12.0
|
||||
|
||||
- show response time in milliseconds per request and total
|
||||
|
||||
## 0.11.0
|
||||
|
||||
- fix(#119) Support for Basic and Bearer Auth
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
{
|
||||
"summary": {
|
||||
"totalRequests": 10,
|
||||
"passedRequests": 10,
|
||||
"failedRequests": 0,
|
||||
"totalAssertions": 4,
|
||||
"passedAssertions": 4,
|
||||
"failedAssertions": 0,
|
||||
"passedAssertions": 0,
|
||||
"failedAssertions": 4,
|
||||
"totalTests": 0,
|
||||
"passedTests": 0,
|
||||
"failedTests": 0
|
||||
@@ -11,53 +14,33 @@
|
||||
{
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"url": "http://localhost:8080/test/v4",
|
||||
"url": "http://localhost:3000/test/v4",
|
||||
"headers": {}
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"statusText": "OK",
|
||||
"status": 404,
|
||||
"statusText": "Not Found",
|
||||
"headers": {
|
||||
"x-powered-by": "Express",
|
||||
"content-type": "application/json; charset=utf-8",
|
||||
"content-length": "497",
|
||||
"etag": "W/\"1f1-08gGpUcq2NTnMCVT5AuXxQ0DzGE\"",
|
||||
"date": "Mon, 25 Sep 2023 21:43:02 GMT",
|
||||
"content-security-policy": "default-src 'none'",
|
||||
"x-content-type-options": "nosniff",
|
||||
"content-type": "text/html; charset=utf-8",
|
||||
"content-length": "146",
|
||||
"date": "Fri, 29 Sep 2023 00:37:50 GMT",
|
||||
"connection": "close"
|
||||
},
|
||||
"data": {
|
||||
"path": "/test/v4",
|
||||
"headers": {
|
||||
"accept": "application/json, text/plain, */*",
|
||||
"user-agent": "axios/1.5.0",
|
||||
"accept-encoding": "gzip, compress, deflate, br",
|
||||
"host": "localhost:8080",
|
||||
"connection": "close"
|
||||
},
|
||||
"method": "GET",
|
||||
"body": "",
|
||||
"fresh": false,
|
||||
"hostname": "localhost",
|
||||
"ip": "",
|
||||
"ips": [],
|
||||
"protocol": "http",
|
||||
"query": {},
|
||||
"subdomains": [],
|
||||
"xhr": false,
|
||||
"os": {
|
||||
"hostname": "05512cb2102c"
|
||||
},
|
||||
"connection": {}
|
||||
}
|
||||
"data": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>Error</title>\n</head>\n<body>\n<pre>Cannot GET /test/v4</pre>\n</body>\n</html>\n"
|
||||
},
|
||||
"error": null,
|
||||
"assertionResults": [
|
||||
{
|
||||
"uid": "mTrKBl5YU6jiAVG-phKT4",
|
||||
"uid": "oidgfXLiyD8Jv0NBAHUHF",
|
||||
"lhsExpr": "res.status",
|
||||
"rhsExpr": "200",
|
||||
"rhsOperand": "200",
|
||||
"operator": "eq",
|
||||
"status": "pass"
|
||||
"status": "fail",
|
||||
"error": "expected 404 to equal 200"
|
||||
}
|
||||
],
|
||||
"testResults": []
|
||||
@@ -65,53 +48,33 @@
|
||||
{
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"url": "http://localhost:8080/test/v2",
|
||||
"url": "http://localhost:3000/test/v2",
|
||||
"headers": {}
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"statusText": "OK",
|
||||
"status": 404,
|
||||
"statusText": "Not Found",
|
||||
"headers": {
|
||||
"x-powered-by": "Express",
|
||||
"content-type": "application/json; charset=utf-8",
|
||||
"content-length": "497",
|
||||
"etag": "W/\"1f1-lMqxZgVOJiQXjF5yk3AFEU8O9Ro\"",
|
||||
"date": "Mon, 25 Sep 2023 21:43:02 GMT",
|
||||
"content-security-policy": "default-src 'none'",
|
||||
"x-content-type-options": "nosniff",
|
||||
"content-type": "text/html; charset=utf-8",
|
||||
"content-length": "146",
|
||||
"date": "Fri, 29 Sep 2023 00:37:50 GMT",
|
||||
"connection": "close"
|
||||
},
|
||||
"data": {
|
||||
"path": "/test/v2",
|
||||
"headers": {
|
||||
"accept": "application/json, text/plain, */*",
|
||||
"user-agent": "axios/1.5.0",
|
||||
"accept-encoding": "gzip, compress, deflate, br",
|
||||
"host": "localhost:8080",
|
||||
"connection": "close"
|
||||
},
|
||||
"method": "GET",
|
||||
"body": "",
|
||||
"fresh": false,
|
||||
"hostname": "localhost",
|
||||
"ip": "",
|
||||
"ips": [],
|
||||
"protocol": "http",
|
||||
"query": {},
|
||||
"subdomains": [],
|
||||
"xhr": false,
|
||||
"os": {
|
||||
"hostname": "05512cb2102c"
|
||||
},
|
||||
"connection": {}
|
||||
}
|
||||
"data": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>Error</title>\n</head>\n<body>\n<pre>Cannot GET /test/v2</pre>\n</body>\n</html>\n"
|
||||
},
|
||||
"error": null,
|
||||
"assertionResults": [
|
||||
{
|
||||
"uid": "XsjjGx9cjt5t8tE_t69ZB",
|
||||
"uid": "IgliYuHd9wKp6JNyqyHFK",
|
||||
"lhsExpr": "res.status",
|
||||
"rhsExpr": "200",
|
||||
"rhsOperand": "200",
|
||||
"operator": "eq",
|
||||
"status": "pass"
|
||||
"status": "fail",
|
||||
"error": "expected 404 to equal 200"
|
||||
}
|
||||
],
|
||||
"testResults": []
|
||||
@@ -119,53 +82,33 @@
|
||||
{
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"url": "http://localhost:8080/test/v3",
|
||||
"url": "http://localhost:3000/test/v3",
|
||||
"headers": {}
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"statusText": "OK",
|
||||
"status": 404,
|
||||
"statusText": "Not Found",
|
||||
"headers": {
|
||||
"x-powered-by": "Express",
|
||||
"content-type": "application/json; charset=utf-8",
|
||||
"content-length": "497",
|
||||
"etag": "W/\"1f1-tSiYu0/vWz3r+NYRCaed0aW1waw\"",
|
||||
"date": "Mon, 25 Sep 2023 21:43:02 GMT",
|
||||
"content-security-policy": "default-src 'none'",
|
||||
"x-content-type-options": "nosniff",
|
||||
"content-type": "text/html; charset=utf-8",
|
||||
"content-length": "146",
|
||||
"date": "Fri, 29 Sep 2023 00:37:50 GMT",
|
||||
"connection": "close"
|
||||
},
|
||||
"data": {
|
||||
"path": "/test/v3",
|
||||
"headers": {
|
||||
"accept": "application/json, text/plain, */*",
|
||||
"user-agent": "axios/1.5.0",
|
||||
"accept-encoding": "gzip, compress, deflate, br",
|
||||
"host": "localhost:8080",
|
||||
"connection": "close"
|
||||
},
|
||||
"method": "GET",
|
||||
"body": "",
|
||||
"fresh": false,
|
||||
"hostname": "localhost",
|
||||
"ip": "",
|
||||
"ips": [],
|
||||
"protocol": "http",
|
||||
"query": {},
|
||||
"subdomains": [],
|
||||
"xhr": false,
|
||||
"os": {
|
||||
"hostname": "05512cb2102c"
|
||||
},
|
||||
"connection": {}
|
||||
}
|
||||
"data": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>Error</title>\n</head>\n<body>\n<pre>Cannot GET /test/v3</pre>\n</body>\n</html>\n"
|
||||
},
|
||||
"error": null,
|
||||
"assertionResults": [
|
||||
{
|
||||
"uid": "i_8MmDMtJA9YfvB_FrW15",
|
||||
"uid": "u-3sRebrCyuUbZOkwS0z8",
|
||||
"lhsExpr": "res.status",
|
||||
"rhsExpr": "200",
|
||||
"rhsOperand": "200",
|
||||
"operator": "eq",
|
||||
"status": "pass"
|
||||
"status": "fail",
|
||||
"error": "expected 404 to equal 200"
|
||||
}
|
||||
],
|
||||
"testResults": []
|
||||
@@ -173,7 +116,7 @@
|
||||
{
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"url": "http://localhost:8080/test/v1",
|
||||
"url": "http://localhost:3000/test/v1",
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
@@ -181,57 +124,201 @@
|
||||
"test": "hello"
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"status": 404,
|
||||
"statusText": "Not Found",
|
||||
"headers": {
|
||||
"x-powered-by": "Express",
|
||||
"content-security-policy": "default-src 'none'",
|
||||
"x-content-type-options": "nosniff",
|
||||
"content-type": "text/html; charset=utf-8",
|
||||
"content-length": "147",
|
||||
"date": "Fri, 29 Sep 2023 00:37:50 GMT",
|
||||
"connection": "close"
|
||||
},
|
||||
"data": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>Error</title>\n</head>\n<body>\n<pre>Cannot POST /test/v1</pre>\n</body>\n</html>\n"
|
||||
},
|
||||
"error": null,
|
||||
"assertionResults": [
|
||||
{
|
||||
"uid": "PpKLK6I38I5_ibw4lZqLb",
|
||||
"lhsExpr": "res.status",
|
||||
"rhsExpr": "eq 200",
|
||||
"rhsOperand": "200",
|
||||
"operator": "eq",
|
||||
"status": "fail",
|
||||
"error": "expected 404 to equal 200"
|
||||
}
|
||||
],
|
||||
"testResults": []
|
||||
},
|
||||
{
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"url": "http://localhost:3000/test",
|
||||
"headers": {}
|
||||
},
|
||||
"response": {
|
||||
"status": 404,
|
||||
"statusText": "Not Found",
|
||||
"headers": {
|
||||
"x-powered-by": "Express",
|
||||
"content-security-policy": "default-src 'none'",
|
||||
"x-content-type-options": "nosniff",
|
||||
"content-type": "text/html; charset=utf-8",
|
||||
"content-length": "144",
|
||||
"date": "Fri, 29 Sep 2023 00:37:50 GMT",
|
||||
"connection": "close"
|
||||
},
|
||||
"data": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>Error</title>\n</head>\n<body>\n<pre>Cannot POST /test</pre>\n</body>\n</html>\n"
|
||||
},
|
||||
"error": null,
|
||||
"assertionResults": [],
|
||||
"testResults": []
|
||||
},
|
||||
{
|
||||
"request": {
|
||||
"method": "HEAD",
|
||||
"url": "http://localhost:3000/",
|
||||
"headers": {}
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"statusText": "OK",
|
||||
"headers": {
|
||||
"x-powered-by": "Express",
|
||||
"content-type": "application/json; charset=utf-8",
|
||||
"content-length": "623",
|
||||
"etag": "W/\"26f-ku5QGz4p9f02u79vJIve7JH3QYM\"",
|
||||
"date": "Mon, 25 Sep 2023 21:43:02 GMT",
|
||||
"content-type": "text/html; charset=utf-8",
|
||||
"content-length": "12",
|
||||
"etag": "W/\"c-Lve95gjOVATpfV8EL5X4nxwjKHE\"",
|
||||
"date": "Fri, 29 Sep 2023 00:37:50 GMT",
|
||||
"connection": "close"
|
||||
},
|
||||
"data": ""
|
||||
},
|
||||
"error": null,
|
||||
"assertionResults": [],
|
||||
"testResults": []
|
||||
},
|
||||
{
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"url": "http://localhost:3000",
|
||||
"headers": {}
|
||||
},
|
||||
"response": {
|
||||
"status": 404,
|
||||
"statusText": "Not Found",
|
||||
"headers": {
|
||||
"x-powered-by": "Express",
|
||||
"content-security-policy": "default-src 'none'",
|
||||
"x-content-type-options": "nosniff",
|
||||
"content-type": "text/html; charset=utf-8",
|
||||
"content-length": "140",
|
||||
"date": "Fri, 29 Sep 2023 00:37:50 GMT",
|
||||
"connection": "close"
|
||||
},
|
||||
"data": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>Error</title>\n</head>\n<body>\n<pre>Cannot POST /</pre>\n</body>\n</html>\n"
|
||||
},
|
||||
"error": null,
|
||||
"assertionResults": [],
|
||||
"testResults": []
|
||||
},
|
||||
{
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"url": "http://localhost:3000/",
|
||||
"headers": {
|
||||
"content-type": "multipart/form-data; boundary=--------------------------897965859410704836065858"
|
||||
},
|
||||
"data": {
|
||||
"path": "/test/v1",
|
||||
"headers": {
|
||||
"accept": "application/json, text/plain, */*",
|
||||
"content-type": "application/json",
|
||||
"user-agent": "axios/1.5.0",
|
||||
"content-length": "16",
|
||||
"accept-encoding": "gzip, compress, deflate, br",
|
||||
"host": "localhost:8080",
|
||||
"connection": "close"
|
||||
},
|
||||
"method": "POST",
|
||||
"body": "{\"test\":\"hello\"}",
|
||||
"fresh": false,
|
||||
"hostname": "localhost",
|
||||
"ip": "",
|
||||
"ips": [],
|
||||
"protocol": "http",
|
||||
"query": {},
|
||||
"subdomains": [],
|
||||
"xhr": false,
|
||||
"os": {
|
||||
"hostname": "05512cb2102c"
|
||||
},
|
||||
"connection": {},
|
||||
"json": {
|
||||
"test": "hello"
|
||||
}
|
||||
"_overheadLength": 103,
|
||||
"_valueLength": 3,
|
||||
"_valuesToMeasure": [],
|
||||
"writable": false,
|
||||
"readable": true,
|
||||
"dataSize": 0,
|
||||
"maxDataSize": 2097152,
|
||||
"pauseStreams": true,
|
||||
"_released": true,
|
||||
"_streams": [],
|
||||
"_currentStream": null,
|
||||
"_insideLoop": false,
|
||||
"_pendingNext": false,
|
||||
"_boundary": "--------------------------897965859410704836065858",
|
||||
"_events": {},
|
||||
"_eventsCount": 3
|
||||
}
|
||||
},
|
||||
"assertionResults": [
|
||||
{
|
||||
"uid": "hNBSF_GBdSTFHNiyCcOn9",
|
||||
"lhsExpr": "res.status",
|
||||
"rhsExpr": "200",
|
||||
"rhsOperand": "200",
|
||||
"operator": "eq",
|
||||
"status": "pass"
|
||||
}
|
||||
],
|
||||
"response": {
|
||||
"status": 404,
|
||||
"statusText": "Not Found",
|
||||
"headers": {
|
||||
"x-powered-by": "Express",
|
||||
"content-security-policy": "default-src 'none'",
|
||||
"x-content-type-options": "nosniff",
|
||||
"content-type": "text/html; charset=utf-8",
|
||||
"content-length": "140",
|
||||
"date": "Fri, 29 Sep 2023 00:37:50 GMT",
|
||||
"connection": "close"
|
||||
},
|
||||
"data": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>Error</title>\n</head>\n<body>\n<pre>Cannot POST /</pre>\n</body>\n</html>\n"
|
||||
},
|
||||
"error": null,
|
||||
"assertionResults": [],
|
||||
"testResults": []
|
||||
},
|
||||
{
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"url": "http://localhost:3000/",
|
||||
"headers": {
|
||||
"content-type": "application/x-www-form-urlencoded"
|
||||
},
|
||||
"data": "a=b&c=d"
|
||||
},
|
||||
"response": {
|
||||
"status": 404,
|
||||
"statusText": "Not Found",
|
||||
"headers": {
|
||||
"x-powered-by": "Express",
|
||||
"content-security-policy": "default-src 'none'",
|
||||
"x-content-type-options": "nosniff",
|
||||
"content-type": "text/html; charset=utf-8",
|
||||
"content-length": "140",
|
||||
"date": "Fri, 29 Sep 2023 00:37:50 GMT",
|
||||
"connection": "close"
|
||||
},
|
||||
"data": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>Error</title>\n</head>\n<body>\n<pre>Cannot POST /</pre>\n</body>\n</html>\n"
|
||||
},
|
||||
"error": null,
|
||||
"assertionResults": [],
|
||||
"testResults": []
|
||||
},
|
||||
{
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"url": "http://localhost:3000/test",
|
||||
"headers": {
|
||||
"content-type": "text/xml"
|
||||
},
|
||||
"data": "<xml>\n <test>1</test>\n</xml>"
|
||||
},
|
||||
"response": {
|
||||
"status": 404,
|
||||
"statusText": "Not Found",
|
||||
"headers": {
|
||||
"x-powered-by": "Express",
|
||||
"content-security-policy": "default-src 'none'",
|
||||
"x-content-type-options": "nosniff",
|
||||
"content-type": "text/html; charset=utf-8",
|
||||
"content-length": "144",
|
||||
"date": "Fri, 29 Sep 2023 00:37:50 GMT",
|
||||
"connection": "close"
|
||||
},
|
||||
"data": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<title>Error</title>\n</head>\n<body>\n<pre>Cannot POST /test</pre>\n</body>\n</html>\n"
|
||||
},
|
||||
"error": null,
|
||||
"assertionResults": [],
|
||||
"testResults": []
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@usebruno/cli",
|
||||
"version": "0.11.0",
|
||||
"version": "0.12.0",
|
||||
"license": "MIT",
|
||||
"main": "src/index.js",
|
||||
"bin": {
|
||||
@@ -13,6 +13,9 @@
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/usebruno/bruno.git"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "jest"
|
||||
},
|
||||
"files": [
|
||||
"src",
|
||||
"bin",
|
||||
@@ -26,6 +29,7 @@
|
||||
"axios": "^1.5.1",
|
||||
"chai": "^4.3.7",
|
||||
"chalk": "^3.0.0",
|
||||
"decomment": "^0.9.5",
|
||||
"form-data": "^4.0.0",
|
||||
"fs-extra": "^10.1.0",
|
||||
"handlebars": "^4.7.8",
|
||||
|
||||
@@ -12,17 +12,56 @@ const { dotenvToJson } = require('@usebruno/lang');
|
||||
const command = 'run [filename]';
|
||||
const desc = 'Run a request';
|
||||
|
||||
const printRunSummary = (assertionResults, testResults) => {
|
||||
// display assertion results and test results summary
|
||||
const totalAssertions = assertionResults.length;
|
||||
const passedAssertions = assertionResults.filter((result) => result.status === 'pass').length;
|
||||
const failedAssertions = totalAssertions - passedAssertions;
|
||||
const printRunSummary = (results) => {
|
||||
let totalRequests = 0;
|
||||
let passedRequests = 0;
|
||||
let failedRequests = 0;
|
||||
let totalAssertions = 0;
|
||||
let passedAssertions = 0;
|
||||
let failedAssertions = 0;
|
||||
let totalTests = 0;
|
||||
let passedTests = 0;
|
||||
let failedTests = 0;
|
||||
|
||||
for (const result of results) {
|
||||
totalRequests += 1;
|
||||
totalTests += result.testResults.length;
|
||||
totalAssertions += result.assertionResults.length;
|
||||
let anyFailed = false;
|
||||
let hasAnyTestsOrAssertions = false;
|
||||
for (const testResult of result.testResults) {
|
||||
hasAnyTestsOrAssertions = true;
|
||||
if (testResult.status === 'pass') {
|
||||
passedTests += 1;
|
||||
} else {
|
||||
anyFailed = true;
|
||||
failedTests += 1;
|
||||
}
|
||||
}
|
||||
for (const assertionResult of result.assertionResults) {
|
||||
hasAnyTestsOrAssertions = true;
|
||||
if (assertionResult.status === 'pass') {
|
||||
passedAssertions += 1;
|
||||
} else {
|
||||
anyFailed = true;
|
||||
failedAssertions += 1;
|
||||
}
|
||||
}
|
||||
if (!hasAnyTestsOrAssertions && result.error) {
|
||||
failedRequests += 1;
|
||||
} else {
|
||||
passedRequests += 1;
|
||||
}
|
||||
}
|
||||
|
||||
const totalTests = testResults.length;
|
||||
const passedTests = testResults.filter((result) => result.status === 'pass').length;
|
||||
const failedTests = totalTests - passedTests;
|
||||
const maxLength = 12;
|
||||
|
||||
let requestSummary = `${rpad('Requests:', maxLength)} ${chalk.green(`${passedRequests} passed`)}`;
|
||||
if (failedRequests > 0) {
|
||||
requestSummary += `, ${chalk.red(`${failedRequests} failed`)}`;
|
||||
}
|
||||
requestSummary += `, ${totalRequests} total`;
|
||||
|
||||
let assertSummary = `${rpad('Tests:', maxLength)} ${chalk.green(`${passedTests} passed`)}`;
|
||||
if (failedTests > 0) {
|
||||
assertSummary += `, ${chalk.red(`${failedTests} failed`)}`;
|
||||
@@ -35,10 +74,14 @@ const printRunSummary = (assertionResults, testResults) => {
|
||||
}
|
||||
testSummary += `, ${totalAssertions} total`;
|
||||
|
||||
console.log('\n' + chalk.bold(assertSummary));
|
||||
console.log('\n' + chalk.bold(requestSummary));
|
||||
console.log(chalk.bold(assertSummary));
|
||||
console.log(chalk.bold(testSummary));
|
||||
|
||||
return {
|
||||
totalRequests,
|
||||
passedRequests,
|
||||
failedRequests,
|
||||
totalAssertions,
|
||||
passedAssertions,
|
||||
failedAssertions,
|
||||
@@ -255,9 +298,7 @@ const handler = async function (argv) {
|
||||
}
|
||||
|
||||
const _isFile = await isFile(filename);
|
||||
let assertionResults = [];
|
||||
let testResults = [];
|
||||
let testrunResults = [];
|
||||
let results = [];
|
||||
|
||||
let bruJsons = [];
|
||||
|
||||
@@ -311,17 +352,12 @@ const handler = async function (argv) {
|
||||
brunoConfig
|
||||
);
|
||||
|
||||
if (result) {
|
||||
testrunResults.push(result);
|
||||
const { assertionResults: _assertionResults, testResults: _testResults } = result;
|
||||
|
||||
assertionResults = assertionResults.concat(_assertionResults);
|
||||
testResults = testResults.concat(_testResults);
|
||||
}
|
||||
results.push(result);
|
||||
}
|
||||
|
||||
const summary = printRunSummary(assertionResults, testResults);
|
||||
console.log(chalk.dim(chalk.grey('Ran all requests.')));
|
||||
const summary = printRunSummary(results);
|
||||
const totalTime = results.reduce((acc, res) => acc + res.response.responseTime, 0);
|
||||
console.log(chalk.dim(chalk.grey(`Ran all requests - ${totalTime} ms`)));
|
||||
|
||||
if (outputPath && outputPath.length) {
|
||||
const outputDir = path.dirname(outputPath);
|
||||
@@ -333,14 +369,14 @@ const handler = async function (argv) {
|
||||
|
||||
const outputJson = {
|
||||
summary,
|
||||
results: testrunResults
|
||||
results
|
||||
};
|
||||
|
||||
fs.writeFileSync(outputPath, JSON.stringify(outputJson, null, 2));
|
||||
console.log(chalk.dim(chalk.grey(`Wrote results to ${outputPath}`)));
|
||||
}
|
||||
|
||||
if (summary.failedAssertions > 0 || summary.failedTests > 0) {
|
||||
if (summary.failedAssertions + summary.failedTests + summary.failedRequests > 0) {
|
||||
process.exit(1);
|
||||
}
|
||||
} catch (err) {
|
||||
@@ -354,5 +390,6 @@ module.exports = {
|
||||
command,
|
||||
desc,
|
||||
builder,
|
||||
handler
|
||||
handler,
|
||||
printRunSummary
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
const { get, each, filter } = require('lodash');
|
||||
const decomment = require('decomment');
|
||||
|
||||
const prepareRequest = (request) => {
|
||||
const headers = {};
|
||||
@@ -39,7 +40,7 @@ const prepareRequest = (request) => {
|
||||
axiosRequest.headers['content-type'] = 'application/json';
|
||||
}
|
||||
try {
|
||||
axiosRequest.data = JSON.parse(request.body.json);
|
||||
axiosRequest.data = JSON.parse(decomment(request.body.json));
|
||||
} catch (ex) {
|
||||
axiosRequest.data = request.body.json;
|
||||
}
|
||||
@@ -78,7 +79,7 @@ const prepareRequest = (request) => {
|
||||
if (request.body.mode === 'graphql') {
|
||||
const graphqlQuery = {
|
||||
query: get(request, 'body.graphql.query'),
|
||||
variables: JSON.parse(get(request, 'body.graphql.variables') || '{}')
|
||||
variables: JSON.parse(decomment(get(request, 'body.graphql.variables') || '{}'))
|
||||
};
|
||||
if (!contentTypeDefined) {
|
||||
axiosRequest.headers['content-type'] = 'application/json';
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
const qs = require('qs');
|
||||
const chalk = require('chalk');
|
||||
const decomment = require('decomment');
|
||||
const fs = require('fs');
|
||||
const { forOwn, each, extend, get } = require('lodash');
|
||||
const FormData = require('form-data');
|
||||
const axios = require('axios');
|
||||
const axios = requir
|
||||
e('axios');
|
||||
const prepareRequest = require('./prepare-request');
|
||||
const interpolateVars = require('./interpolate-vars');
|
||||
const { ScriptRuntime, TestRuntime, VarsRuntime, AssertRuntime } = require('@usebruno/js');
|
||||
@@ -12,6 +14,7 @@ const { getOptions } = require('../utils/bru');
|
||||
const https = require('https');
|
||||
const { HttpsProxyAgent } = require('https-proxy-agent');
|
||||
const { HttpProxyAgent } = require('http-proxy-agent');
|
||||
const { makeAxiosInstance } = require('../utils/axios-instance');
|
||||
|
||||
const runSingleRequest = async function (
|
||||
filename,
|
||||
@@ -22,9 +25,9 @@ const runSingleRequest = async function (
|
||||
processEnvVars,
|
||||
brunoConfig
|
||||
) {
|
||||
let request;
|
||||
|
||||
try {
|
||||
let request;
|
||||
|
||||
request = prepareRequest(bruJson.request);
|
||||
|
||||
// make axios work in node using form data
|
||||
@@ -57,7 +60,7 @@ const runSingleRequest = async function (
|
||||
if (requestScriptFile && requestScriptFile.length) {
|
||||
const scriptRuntime = new ScriptRuntime();
|
||||
await scriptRuntime.runRequestScript(
|
||||
requestScriptFile,
|
||||
decomment(requestScriptFile),
|
||||
request,
|
||||
envVariables,
|
||||
collectionVariables,
|
||||
@@ -124,10 +127,48 @@ const runSingleRequest = async function (
|
||||
request.data = qs.stringify(request.data);
|
||||
}
|
||||
|
||||
// run request
|
||||
const response = await axios(request);
|
||||
let response, responseTime;
|
||||
try {
|
||||
// run request
|
||||
const axiosInstance = makeAxiosInstance();
|
||||
|
||||
console.log(chalk.green(stripExtension(filename)) + chalk.dim(` (${response.status} ${response.statusText})`));
|
||||
/** @type {import('axios').AxiosResponse} */
|
||||
response = await axiosInstance(request);
|
||||
|
||||
// Prevents the duration on leaking to the actual result
|
||||
responseTime = response.headers.get('request-duration');
|
||||
response.headers.delete('request-duration');
|
||||
} catch (err) {
|
||||
if (err && err.response) {
|
||||
response = err.response;
|
||||
|
||||
// Prevents the duration on leaking to the actual result
|
||||
responseTime = response.headers.get('request-duration');
|
||||
response.headers.delete('request-duration');
|
||||
} else {
|
||||
console.log(chalk.red(stripExtension(filename)) + chalk.dim(` (${err.message})`));
|
||||
return {
|
||||
request: {
|
||||
method: request.method,
|
||||
url: request.url,
|
||||
headers: request.headers,
|
||||
data: request.data
|
||||
},
|
||||
response: {
|
||||
status: null,
|
||||
statusText: null,
|
||||
headers: null,
|
||||
data: null,
|
||||
responseTime: 0
|
||||
},
|
||||
error: err.message,
|
||||
assertionResults: [],
|
||||
testResults: []
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
console.log(chalk.green(stripExtension(filename)) + chalk.dim(` (${response.status} ${response.statusText}) - ${responseTime} ms`));
|
||||
|
||||
// run post-response vars
|
||||
const postResponseVars = get(bruJson, 'request.vars.res');
|
||||
@@ -149,7 +190,7 @@ const runSingleRequest = async function (
|
||||
if (responseScriptFile && responseScriptFile.length) {
|
||||
const scriptRuntime = new ScriptRuntime();
|
||||
await scriptRuntime.runResponseScript(
|
||||
responseScriptFile,
|
||||
decomment(responseScriptFile),
|
||||
request,
|
||||
response,
|
||||
envVariables,
|
||||
@@ -190,7 +231,7 @@ const runSingleRequest = async function (
|
||||
if (typeof testFile === 'string') {
|
||||
const testRuntime = new TestRuntime();
|
||||
const result = await testRuntime.runTests(
|
||||
testFile,
|
||||
decomment(testFile),
|
||||
request,
|
||||
response,
|
||||
envVariables,
|
||||
@@ -223,102 +264,32 @@ const runSingleRequest = async function (
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
headers: response.headers,
|
||||
data: response.data
|
||||
data: response.data,
|
||||
responseTime
|
||||
},
|
||||
error: null,
|
||||
assertionResults,
|
||||
testResults
|
||||
};
|
||||
} catch (err) {
|
||||
if (err && err.response) {
|
||||
console.log(
|
||||
chalk.green(stripExtension(filename)) + chalk.dim(` (${err.response.status} ${err.response.statusText})`)
|
||||
);
|
||||
|
||||
// run post-response vars
|
||||
const postResponseVars = get(bruJson, 'request.vars.res');
|
||||
if (postResponseVars && postResponseVars.length) {
|
||||
const varsRuntime = new VarsRuntime();
|
||||
varsRuntime.runPostResponseVars(
|
||||
postResponseVars,
|
||||
request,
|
||||
err.response,
|
||||
envVariables,
|
||||
collectionVariables,
|
||||
collectionPath,
|
||||
processEnvVars
|
||||
);
|
||||
}
|
||||
|
||||
// run post response script
|
||||
const responseScriptFile = get(bruJson, 'request.script.res');
|
||||
if (responseScriptFile && responseScriptFile.length) {
|
||||
const scriptRuntime = new ScriptRuntime();
|
||||
await scriptRuntime.runResponseScript(
|
||||
responseScriptFile,
|
||||
request,
|
||||
err.response,
|
||||
envVariables,
|
||||
collectionVariables,
|
||||
collectionPath,
|
||||
null,
|
||||
processEnvVars
|
||||
);
|
||||
}
|
||||
|
||||
// run assertions
|
||||
let assertionResults = [];
|
||||
const assertions = get(bruJson, 'request.assertions');
|
||||
if (assertions) {
|
||||
const assertRuntime = new AssertRuntime();
|
||||
assertionResults = assertRuntime.runAssertions(
|
||||
assertions,
|
||||
request,
|
||||
err.response,
|
||||
envVariables,
|
||||
collectionVariables,
|
||||
collectionPath
|
||||
);
|
||||
|
||||
each(assertionResults, (r) => {
|
||||
if (r.status === 'pass') {
|
||||
console.log(chalk.green(` ✓ `) + chalk.dim(`assert: ${r.lhsExpr}: ${r.rhsExpr}`));
|
||||
} else {
|
||||
console.log(chalk.red(` ✕ `) + chalk.red(`assert: ${r.lhsExpr}: ${r.rhsExpr}`));
|
||||
console.log(chalk.red(` ${r.error}`));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// run tests
|
||||
let testResults = [];
|
||||
const testFile = get(bruJson, 'request.tests');
|
||||
if (typeof testFile === 'string') {
|
||||
const testRuntime = new TestRuntime();
|
||||
const result = await testRuntime.runTests(
|
||||
testFile,
|
||||
request,
|
||||
err.response,
|
||||
envVariables,
|
||||
collectionVariables,
|
||||
collectionPath,
|
||||
null,
|
||||
processEnvVars
|
||||
);
|
||||
testResults = get(result, 'results', []);
|
||||
}
|
||||
|
||||
if (testResults && testResults.length) {
|
||||
each(testResults, (testResult) => {
|
||||
if (testResult.status === 'pass') {
|
||||
console.log(chalk.green(` ✓ `) + chalk.dim(testResult.description));
|
||||
} else {
|
||||
console.log(chalk.red(` ✕ `) + chalk.red(testResult.description));
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
console.log(chalk.red(stripExtension(filename)) + chalk.dim(` (${err.message})`));
|
||||
}
|
||||
return {
|
||||
request: {
|
||||
method: null,
|
||||
url: null,
|
||||
headers: null,
|
||||
data: null
|
||||
},
|
||||
response: {
|
||||
status: null,
|
||||
statusText: null,
|
||||
headers: null,
|
||||
data: null,
|
||||
responseTime: 0
|
||||
},
|
||||
error: err.message,
|
||||
assertionResults: [],
|
||||
testResults: []
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
40
packages/bruno-cli/src/utils/axios-instance.js
Normal file
40
packages/bruno-cli/src/utils/axios-instance.js
Normal file
@@ -0,0 +1,40 @@
|
||||
const axios = require('axios');
|
||||
|
||||
/**
|
||||
* Function that configures axios with timing interceptors
|
||||
* Important to note here that the timings are not completely accurate.
|
||||
* @see https://github.com/axios/axios/issues/695
|
||||
* @returns {import('axios').AxiosStatic}
|
||||
*/
|
||||
function makeAxiosInstance() {
|
||||
/** @type {import('axios').AxiosStatic} */
|
||||
const instance = axios.create();
|
||||
|
||||
instance.interceptors.request.use((config) => {
|
||||
config.headers['request-start-time'] = Date.now();
|
||||
return config;
|
||||
});
|
||||
|
||||
instance.interceptors.response.use(
|
||||
(response) => {
|
||||
const end = Date.now();
|
||||
const start = response.config.headers['request-start-time'];
|
||||
response.headers['request-duration'] = end - start;
|
||||
return response;
|
||||
},
|
||||
(error) => {
|
||||
if (error.response) {
|
||||
const end = Date.now();
|
||||
const start = error.config.headers['request-start-time'];
|
||||
error.response.headers['request-duration'] = end - start;
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
makeAxiosInstance
|
||||
};
|
||||
67
packages/bruno-cli/tests/commands/run.spec.js
Normal file
67
packages/bruno-cli/tests/commands/run.spec.js
Normal file
@@ -0,0 +1,67 @@
|
||||
const { describe, it, expect } = require('@jest/globals');
|
||||
|
||||
const { printRunSummary } = require('../../src/commands/run');
|
||||
|
||||
describe('printRunSummary', () => {
|
||||
// Suppress console.log output
|
||||
jest.spyOn(console, 'log').mockImplementation(() => {});
|
||||
|
||||
it('should produce the correct summary for a successful run', () => {
|
||||
const results = [
|
||||
{
|
||||
testResults: [{ status: 'pass' }, { status: 'pass' }, { status: 'pass' }],
|
||||
assertionResults: [{ status: 'pass' }, { status: 'pass' }],
|
||||
error: null
|
||||
},
|
||||
{
|
||||
testResults: [{ status: 'pass' }, { status: 'pass' }],
|
||||
assertionResults: [{ status: 'pass' }, { status: 'pass' }, { status: 'pass' }],
|
||||
error: null
|
||||
}
|
||||
];
|
||||
|
||||
const summary = printRunSummary(results);
|
||||
|
||||
expect(summary.totalRequests).toBe(2);
|
||||
expect(summary.passedRequests).toBe(2);
|
||||
expect(summary.failedRequests).toBe(0);
|
||||
expect(summary.totalAssertions).toBe(5);
|
||||
expect(summary.passedAssertions).toBe(5);
|
||||
expect(summary.failedAssertions).toBe(0);
|
||||
expect(summary.totalTests).toBe(5);
|
||||
expect(summary.passedTests).toBe(5);
|
||||
expect(summary.failedTests).toBe(0);
|
||||
});
|
||||
|
||||
it('should produce the correct summary for a failed run', () => {
|
||||
const results = [
|
||||
{
|
||||
testResults: [{ status: 'fail' }, { status: 'pass' }, { status: 'pass' }],
|
||||
assertionResults: [{ status: 'pass' }, { status: 'fail' }],
|
||||
error: null
|
||||
},
|
||||
{
|
||||
testResults: [{ status: 'pass' }, { status: 'fail' }],
|
||||
assertionResults: [{ status: 'pass' }, { status: 'fail' }, { status: 'fail' }],
|
||||
error: null
|
||||
},
|
||||
{
|
||||
testResults: [],
|
||||
assertionResults: [],
|
||||
error: new Error('Request failed')
|
||||
}
|
||||
];
|
||||
|
||||
const summary = printRunSummary(results);
|
||||
|
||||
expect(summary.totalRequests).toBe(3);
|
||||
expect(summary.passedRequests).toBe(2);
|
||||
expect(summary.failedRequests).toBe(1);
|
||||
expect(summary.totalAssertions).toBe(5);
|
||||
expect(summary.passedAssertions).toBe(2);
|
||||
expect(summary.failedAssertions).toBe(3);
|
||||
expect(summary.totalTests).toBe(5);
|
||||
expect(summary.passedTests).toBe(3);
|
||||
expect(summary.failedTests).toBe(2);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user