forked from extern/bruno
Merge branch 'main' into feature/license_all_modules
This commit is contained in:
commit
cbb975d81d
@ -1,5 +1,9 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.9.0
|
||||||
|
|
||||||
|
- `--output` flag to collect the results of your API tests
|
||||||
|
|
||||||
## 0.8.0
|
## 0.8.0
|
||||||
|
|
||||||
- `--env-var` flag to set environment variables
|
- `--env-var` flag to set environment variables
|
||||||
|
238
packages/bruno-cli/examples/report.json
Normal file
238
packages/bruno-cli/examples/report.json
Normal file
@ -0,0 +1,238 @@
|
|||||||
|
{
|
||||||
|
"summary": {
|
||||||
|
"totalAssertions": 4,
|
||||||
|
"passedAssertions": 4,
|
||||||
|
"failedAssertions": 0,
|
||||||
|
"totalTests": 0,
|
||||||
|
"passedTests": 0,
|
||||||
|
"failedTests": 0
|
||||||
|
},
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"url": "http://localhost:8080/test/v4",
|
||||||
|
"headers": {}
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"status": 200,
|
||||||
|
"statusText": "OK",
|
||||||
|
"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",
|
||||||
|
"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": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"assertionResults": [
|
||||||
|
{
|
||||||
|
"uid": "mTrKBl5YU6jiAVG-phKT4",
|
||||||
|
"lhsExpr": "res.status",
|
||||||
|
"rhsExpr": "200",
|
||||||
|
"rhsOperand": "200",
|
||||||
|
"operator": "eq",
|
||||||
|
"status": "pass"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"testResults": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"url": "http://localhost:8080/test/v2",
|
||||||
|
"headers": {}
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"status": 200,
|
||||||
|
"statusText": "OK",
|
||||||
|
"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",
|
||||||
|
"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": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"assertionResults": [
|
||||||
|
{
|
||||||
|
"uid": "XsjjGx9cjt5t8tE_t69ZB",
|
||||||
|
"lhsExpr": "res.status",
|
||||||
|
"rhsExpr": "200",
|
||||||
|
"rhsOperand": "200",
|
||||||
|
"operator": "eq",
|
||||||
|
"status": "pass"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"testResults": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"request": {
|
||||||
|
"method": "GET",
|
||||||
|
"url": "http://localhost:8080/test/v3",
|
||||||
|
"headers": {}
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"status": 200,
|
||||||
|
"statusText": "OK",
|
||||||
|
"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",
|
||||||
|
"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": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"assertionResults": [
|
||||||
|
{
|
||||||
|
"uid": "i_8MmDMtJA9YfvB_FrW15",
|
||||||
|
"lhsExpr": "res.status",
|
||||||
|
"rhsExpr": "200",
|
||||||
|
"rhsOperand": "200",
|
||||||
|
"operator": "eq",
|
||||||
|
"status": "pass"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"testResults": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"request": {
|
||||||
|
"method": "POST",
|
||||||
|
"url": "http://localhost:8080/test/v1",
|
||||||
|
"headers": {
|
||||||
|
"content-type": "application/json"
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"test": "hello"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"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",
|
||||||
|
"connection": "close"
|
||||||
|
},
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"assertionResults": [
|
||||||
|
{
|
||||||
|
"uid": "hNBSF_GBdSTFHNiyCcOn9",
|
||||||
|
"lhsExpr": "res.status",
|
||||||
|
"rhsExpr": "200",
|
||||||
|
"rhsOperand": "200",
|
||||||
|
"operator": "eq",
|
||||||
|
"status": "pass"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"testResults": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@usebruno/cli",
|
"name": "@usebruno/cli",
|
||||||
"version": "0.8.0",
|
"version": "0.9.0",
|
||||||
"license" : "MIT",
|
"license" : "MIT",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"bin": {
|
"bin": {
|
||||||
|
@ -5,16 +5,21 @@ With Bruno CLI, you can now run your API collections with ease using simple comm
|
|||||||
This makes it easier to test your APIs in different environments, automate your testing process, and integrate your API tests with your continuous integration and deployment workflows.
|
This makes it easier to test your APIs in different environments, automate your testing process, and integrate your API tests with your continuous integration and deployment workflows.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
To install the Bruno CLI, use the node package manager of your choice, such as NPM:
|
To install the Bruno CLI, use the node package manager of your choice, such as NPM:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install -g @usebruno/cli
|
npm install -g @usebruno/cli
|
||||||
```
|
```
|
||||||
|
|
||||||
## Getting started
|
## Getting started
|
||||||
|
|
||||||
Navigate to the directory where your API collection resides, and then run:
|
Navigate to the directory where your API collection resides, and then run:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bru run
|
bru run
|
||||||
```
|
```
|
||||||
|
|
||||||
This command will run all the requests in your collection. You can also run a single request by specifying its filename:
|
This command will run all the requests in your collection. You can also run a single request by specifying its filename:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@ -22,25 +27,37 @@ bru run request.bru
|
|||||||
```
|
```
|
||||||
|
|
||||||
Or run all requests in a collection's subfolder:
|
Or run all requests in a collection's subfolder:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bru run folder
|
bru run folder
|
||||||
```
|
```
|
||||||
|
|
||||||
If you need to use an environment, you can specify it with the --env option:
|
If you need to use an environment, you can specify it with the --env option:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bru run folder --env Local
|
bru run folder --env Local
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If you need to collect the results of your API tests, you can specify the --output option:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bru run folder --output results.json
|
||||||
|
```
|
||||||
|
|
||||||
## Demo
|
## Demo
|
||||||
|
|
||||||
![demo](assets/images/cli-demo.png)
|
![demo](assets/images/cli-demo.png)
|
||||||
|
|
||||||
## Support
|
## Support
|
||||||
|
|
||||||
If you encounter any issues or have any feedback or suggestions, please raise them on our [GitHub repository](https://github.com/usebruno/bruno)
|
If you encounter any issues or have any feedback or suggestions, please raise them on our [GitHub repository](https://github.com/usebruno/bruno)
|
||||||
|
|
||||||
Thank you for using Bruno CLI!
|
Thank you for using Bruno CLI!
|
||||||
|
|
||||||
## Changelog
|
## Changelog
|
||||||
|
|
||||||
See [here](packages/bruno-cli/changelog.md)
|
See [here](packages/bruno-cli/changelog.md)
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
[MIT](license.md)
|
[MIT](license.md)
|
@ -127,6 +127,11 @@ const builder = async (yargs) => {
|
|||||||
describe: 'Overwrite a single environment variable, multiple usages possible',
|
describe: 'Overwrite a single environment variable, multiple usages possible',
|
||||||
type: 'string'
|
type: 'string'
|
||||||
})
|
})
|
||||||
|
.option('output', {
|
||||||
|
alias: 'o',
|
||||||
|
describe: 'Path to write JSON results to',
|
||||||
|
type: 'string'
|
||||||
|
})
|
||||||
.option('insecure', {
|
.option('insecure', {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
description: 'Allow insecure server connections'
|
description: 'Allow insecure server connections'
|
||||||
@ -138,12 +143,16 @@ const builder = async (yargs) => {
|
|||||||
.example(
|
.example(
|
||||||
'$0 run request.bru --env local --env-var secret=xxx',
|
'$0 run request.bru --env local --env-var secret=xxx',
|
||||||
'Run a request with the environment set to local and overwrite the variable secret with value xxx'
|
'Run a request with the environment set to local and overwrite the variable secret with value xxx'
|
||||||
|
)
|
||||||
|
.example(
|
||||||
|
'$0 run request.bru --output results.json',
|
||||||
|
'Run a request and write the results to results.json in the current directory'
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handler = async function (argv) {
|
const handler = async function (argv) {
|
||||||
try {
|
try {
|
||||||
let { filename, cacert, env, envVar, insecure, r: recursive } = argv;
|
let { filename, cacert, env, envVar, insecure, r: recursive, output: outputPath } = argv;
|
||||||
const collectionPath = process.cwd();
|
const collectionPath = process.cwd();
|
||||||
|
|
||||||
// todo
|
// todo
|
||||||
@ -243,36 +252,24 @@ const handler = async function (argv) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const _isFile = await isFile(filename);
|
const _isFile = await isFile(filename);
|
||||||
|
let assertionResults = [];
|
||||||
|
let testResults = [];
|
||||||
|
let testrunResults = [];
|
||||||
|
|
||||||
|
let bruJsons = [];
|
||||||
|
|
||||||
if (_isFile) {
|
if (_isFile) {
|
||||||
console.log(chalk.yellow('Running Request \n'));
|
console.log(chalk.yellow('Running Request \n'));
|
||||||
const bruContent = fs.readFileSync(filename, 'utf8');
|
const bruContent = fs.readFileSync(filename, 'utf8');
|
||||||
const bruJson = bruToJson(bruContent);
|
const bruJson = bruToJson(bruContent);
|
||||||
const result = await runSingleRequest(
|
bruJsons.push({
|
||||||
filename,
|
bruFilepath: filename,
|
||||||
bruJson,
|
bruJson
|
||||||
collectionPath,
|
});
|
||||||
collectionVariables,
|
|
||||||
envVars,
|
|
||||||
processEnvVars
|
|
||||||
);
|
|
||||||
|
|
||||||
if (result) {
|
|
||||||
const { assertionResults, testResults } = result;
|
|
||||||
|
|
||||||
const summary = printRunSummary(assertionResults, testResults);
|
|
||||||
console.log(chalk.dim(chalk.grey('Done.')));
|
|
||||||
|
|
||||||
if (summary.failedAssertions > 0 || summary.failedTests > 0) {
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const _isDirectory = await isDirectory(filename);
|
const _isDirectory = await isDirectory(filename);
|
||||||
if (_isDirectory) {
|
if (_isDirectory) {
|
||||||
let bruJsons = [];
|
|
||||||
if (!recursive) {
|
if (!recursive) {
|
||||||
console.log(chalk.yellow('Running Folder \n'));
|
console.log(chalk.yellow('Running Folder \n'));
|
||||||
const files = fs.readdirSync(filename);
|
const files = fs.readdirSync(filename);
|
||||||
@ -287,8 +284,6 @@ const handler = async function (argv) {
|
|||||||
bruJson
|
bruJson
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// order requests by sequence
|
|
||||||
bruJsons.sort((a, b) => {
|
bruJsons.sort((a, b) => {
|
||||||
const aSequence = a.bruJson.seq || 0;
|
const aSequence = a.bruJson.seq || 0;
|
||||||
const bSequence = b.bruJson.seq || 0;
|
const bSequence = b.bruJson.seq || 0;
|
||||||
@ -299,9 +294,7 @@ const handler = async function (argv) {
|
|||||||
|
|
||||||
bruJsons = getBruFilesRecursively(filename);
|
bruJsons = getBruFilesRecursively(filename);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
let assertionResults = [];
|
|
||||||
let testResults = [];
|
|
||||||
|
|
||||||
for (const iter of bruJsons) {
|
for (const iter of bruJsons) {
|
||||||
const { bruFilepath, bruJson } = iter;
|
const { bruFilepath, bruJson } = iter;
|
||||||
@ -315,6 +308,7 @@ const handler = async function (argv) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
|
testrunResults.push(result);
|
||||||
const { assertionResults: _assertionResults, testResults: _testResults } = result;
|
const { assertionResults: _assertionResults, testResults: _testResults } = result;
|
||||||
|
|
||||||
assertionResults = assertionResults.concat(_assertionResults);
|
assertionResults = assertionResults.concat(_assertionResults);
|
||||||
@ -325,9 +319,25 @@ const handler = async function (argv) {
|
|||||||
const summary = printRunSummary(assertionResults, testResults);
|
const summary = printRunSummary(assertionResults, testResults);
|
||||||
console.log(chalk.dim(chalk.grey('Ran all requests.')));
|
console.log(chalk.dim(chalk.grey('Ran all requests.')));
|
||||||
|
|
||||||
if (summary.failedAssertions > 0 || summary.failedTests > 0) {
|
if (outputPath && outputPath.length) {
|
||||||
|
const outputDir = path.dirname(outputPath);
|
||||||
|
const outputDirExists = await exists(outputDir);
|
||||||
|
if (!outputDirExists) {
|
||||||
|
console.error(chalk.red(`Output directory ${outputDir} does not exist`));
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const outputJson = {
|
||||||
|
summary,
|
||||||
|
results: testrunResults
|
||||||
|
};
|
||||||
|
|
||||||
|
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) {
|
||||||
|
process.exit(1);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log('Something went wrong');
|
console.log('Something went wrong');
|
||||||
|
@ -171,6 +171,18 @@ const runSingleRequest = async function (
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
request: {
|
||||||
|
method: request.method,
|
||||||
|
url: request.url,
|
||||||
|
headers: request.headers,
|
||||||
|
data: request.data
|
||||||
|
},
|
||||||
|
response: {
|
||||||
|
status: response.status,
|
||||||
|
statusText: response.statusText,
|
||||||
|
headers: response.headers,
|
||||||
|
data: response.data
|
||||||
|
},
|
||||||
assertionResults,
|
assertionResults,
|
||||||
testResults
|
testResults
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user