From 9bcf56d6894be1fc4e4c1d6336332b9c199be8dc Mon Sep 17 00:00:00 2001 From: Thomas Pyle Date: Thu, 28 Sep 2023 20:42:48 -0400 Subject: [PATCH 01/23] Handle failed requests and reduce duplication --- packages/bruno-cli/examples/report.json | 373 +++++++++++------- packages/bruno-cli/src/commands/run.js | 79 ++-- .../src/runner/run-single-request.js | 142 +++---- 3 files changed, 335 insertions(+), 259 deletions(-) diff --git a/packages/bruno-cli/examples/report.json b/packages/bruno-cli/examples/report.json index 3dfa641f2..4cb7586b1 100644 --- a/packages/bruno-cli/examples/report.json +++ b/packages/bruno-cli/examples/report.json @@ -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": "\n\n\n\nError\n\n\n
Cannot GET /test/v4
\n\n\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": "\n\n\n\nError\n\n\n
Cannot GET /test/v2
\n\n\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": "\n\n\n\nError\n\n\n
Cannot GET /test/v3
\n\n\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": "\n\n\n\nError\n\n\n
Cannot POST /test/v1
\n\n\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": "\n\n\n\nError\n\n\n
Cannot POST /test
\n\n\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": "\n\n\n\nError\n\n\n
Cannot POST /
\n\n\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": "\n\n\n\nError\n\n\n
Cannot POST /
\n\n\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": "\n\n\n\nError\n\n\n
Cannot POST /
\n\n\n" + }, + "error": null, + "assertionResults": [], + "testResults": [] + }, + { + "request": { + "method": "POST", + "url": "http://localhost:3000/test", + "headers": { + "content-type": "text/xml" + }, + "data": "\n 1\n" + }, + "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": "\n\n\n\nError\n\n\n
Cannot POST /test
\n\n\n" + }, + "error": null, + "assertionResults": [], "testResults": [] } ] diff --git a/packages/bruno-cli/src/commands/run.js b/packages/bruno-cli/src/commands/run.js index 087b85d48..5ed53e343 100644 --- a/packages/bruno-cli/src/commands/run.js +++ b/packages/bruno-cli/src/commands/run.js @@ -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,16 +352,10 @@ 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); + const summary = printRunSummary(results); console.log(chalk.dim(chalk.grey('Ran all requests.'))); if (outputPath && outputPath.length) { @@ -333,14 +368,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) { diff --git a/packages/bruno-cli/src/runner/run-single-request.js b/packages/bruno-cli/src/runner/run-single-request.js index e8da96cf1..ddde92a4d 100644 --- a/packages/bruno-cli/src/runner/run-single-request.js +++ b/packages/bruno-cli/src/runner/run-single-request.js @@ -20,9 +20,9 @@ const runSingleRequest = async function ( processEnvVars, brunoConfig ) { - let request; - try { + let request; + request = prepareRequest(bruJson.request); // make axios work in node using form data @@ -122,8 +122,34 @@ const runSingleRequest = async function ( request.data = qs.stringify(request.data); } - // run request - const response = await axios(request); + let response; + try { + // run request + response = await axios(request); + } catch (err) { + if (err && err.response) { + response = err.response; + } 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 + }, + error: err.message, + assertionResults: [], + testResults: [] + }; + } + } console.log(chalk.green(stripExtension(filename)) + chalk.dim(` (${response.status} ${response.statusText})`)); @@ -223,100 +249,28 @@ const runSingleRequest = async function ( headers: response.headers, data: response.data }, + 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 && assertions.length) { - 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 (testFile && testFile.length) { - const testRuntime = new TestRuntime(); - const result = 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 + }, + error: err.message, + assertionResults: [], + testResults: [] + }; } }; From 5f32924ddbec915f7c0e8eac3f212ee1630c7535 Mon Sep 17 00:00:00 2001 From: Thomas Pyle Date: Thu, 28 Sep 2023 20:58:25 -0400 Subject: [PATCH 02/23] Adds some simple unit tests around printRunSummary --- .github/workflows/unit-tests.yml | 42 +++++++------ packages/bruno-cli/package.json | 3 + packages/bruno-cli/src/commands/run.js | 3 +- packages/bruno-cli/src/commands/run.test.js | 67 +++++++++++++++++++++ 4 files changed, 94 insertions(+), 21 deletions(-) create mode 100644 packages/bruno-cli/src/commands/run.test.js diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 49b558f66..c9f7a9ada 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -1,29 +1,31 @@ name: Unit Tests on: push: - branches: [ main ] + branches: [main] pull_request: - branches: [ main ] + branches: [main] jobs: test: timeout-minutes: 60 runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - with: - node-version: 16 - - name: Install dependencies - run: npm i --legacy-peer-deps - - name: Test Package bruno-query - run: npm run test --workspace=packages/bruno-query - - name: Build Package bruno-query - run: npm run build --workspace=packages/bruno-query - - name: Test Package bruno-lang - run: npm run test --workspace=packages/bruno-lang - - name: Test Package bruno-schema - run: npm run test --workspace=packages/bruno-schema - - name: Test Package bruno-app - run: npm run test --workspace=packages/bruno-app - - name: Test Package bruno-js - run: npm run test --workspace=packages/bruno-js + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 16 + - name: Install dependencies + run: npm i --legacy-peer-deps + - name: Test Package bruno-query + run: npm run test --workspace=packages/bruno-query + - name: Build Package bruno-query + run: npm run build --workspace=packages/bruno-query + - name: Test Package bruno-lang + run: npm run test --workspace=packages/bruno-lang + - name: Test Package bruno-schema + run: npm run test --workspace=packages/bruno-schema + - name: Test Package bruno-app + run: npm run test --workspace=packages/bruno-app + - name: Test Package bruno-js + run: npm run test --workspace=packages/bruno-js + - name: Test Package bruno-cli + run: npm run test --workspace=packages/bruno-cli diff --git a/packages/bruno-cli/package.json b/packages/bruno-cli/package.json index 91ce4a19c..fb134ae82 100644 --- a/packages/bruno-cli/package.json +++ b/packages/bruno-cli/package.json @@ -13,6 +13,9 @@ "type": "git", "url": "git+https://github.com/usebruno/bruno.git" }, + "scripts": { + "test": "jest" + }, "files": [ "src", "bin", diff --git a/packages/bruno-cli/src/commands/run.js b/packages/bruno-cli/src/commands/run.js index 5ed53e343..ff0ab42ea 100644 --- a/packages/bruno-cli/src/commands/run.js +++ b/packages/bruno-cli/src/commands/run.js @@ -389,5 +389,6 @@ module.exports = { command, desc, builder, - handler + handler, + printRunSummary }; diff --git a/packages/bruno-cli/src/commands/run.test.js b/packages/bruno-cli/src/commands/run.test.js new file mode 100644 index 000000000..979aec5a3 --- /dev/null +++ b/packages/bruno-cli/src/commands/run.test.js @@ -0,0 +1,67 @@ +const { describe, it, expect } = require('@jest/globals'); + +const { printRunSummary } = require('./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); + }); +}); From 51cb930b6a6b12e85e0d53553a7d4a9e8fa5d29f Mon Sep 17 00:00:00 2001 From: Its-treason <39559178+Its-treason@users.noreply.github.com> Date: Thu, 5 Oct 2023 01:57:02 +0200 Subject: [PATCH 03/23] feat(#111): Automaticly open a newly created request --- .../providers/App/useCollectionTreeSync.js | 37 +++++++++++++++++-- .../ReduxStore/slices/collections/actions.js | 7 +++- .../ReduxStore/slices/collections/index.js | 8 +++- 3 files changed, 46 insertions(+), 6 deletions(-) diff --git a/packages/bruno-app/src/providers/App/useCollectionTreeSync.js b/packages/bruno-app/src/providers/App/useCollectionTreeSync.js index caf057d5b..89fdaabb5 100644 --- a/packages/bruno-app/src/providers/App/useCollectionTreeSync.js +++ b/packages/bruno-app/src/providers/App/useCollectionTreeSync.js @@ -1,5 +1,5 @@ import { useEffect } from 'react'; -import { useDispatch } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; import { collectionAddDirectoryEvent, collectionAddFileEvent, @@ -17,9 +17,15 @@ import { import toast from 'react-hot-toast'; import { openCollectionEvent, collectionAddEnvFileEvent } from 'providers/ReduxStore/slices/collections/actions'; import { isElectron } from 'utils/common/platform'; +import { updateNewRequest } from 'providers/ReduxStore/slices/collections/index'; +import { addTab } from 'providers/ReduxStore/slices/tabs'; +import { getDefaultRequestPaneTab } from 'utils/collections/index'; +import { hideHomePage } from 'providers/ReduxStore/slices/app'; const useCollectionTreeSync = () => { const dispatch = useDispatch(); + const tabs = useSelector((state) => state.tabs.tabs); + const newRequestName = useSelector((state) => state.collections.newRequestName); useEffect(() => { if (!isElectron()) { @@ -50,6 +56,22 @@ const useCollectionTreeSync = () => { file: val }) ); + + // Remove the newRequestName so no random stuff happens + dispatch( + updateNewRequest({ newRequestName: null }) + ); + // When the request was just created open it in a new tab + if (newRequestName === val.data.name) { + dispatch( + addTab({ + uid: val.data.uid, + collectionUid: val.meta.collectionUid, + requestPaneTab: getDefaultRequestPaneTab(val.data) + }) + ); + dispatch(hideHomePage()); + } } if (type === 'change') { dispatch( @@ -115,8 +137,6 @@ const useCollectionTreeSync = () => { dispatch(runRequestEvent(val)); }; - ipcRenderer.invoke('renderer:ready'); - const removeListener1 = ipcRenderer.on('main:collection-opened', _openCollection); const removeListener2 = ipcRenderer.on('main:collection-tree-updated', _collectionTreeUpdated); const removeListener3 = ipcRenderer.on('main:collection-already-opened', _collectionAlreadyOpened); @@ -144,7 +164,16 @@ const useCollectionTreeSync = () => { removeListener10(); removeListener11(); }; - }, [isElectron]); + }, [isElectron, tabs, newRequestName]); + + useEffect(() => { + if (!isElectron()) { + return () => {}; + } + + const { ipcRenderer } = window; + ipcRenderer.invoke('renderer:ready'); + }, []); }; export default useCollectionTreeSync; diff --git a/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js b/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js index ccdd7fe1a..3ad39e676 100644 --- a/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js +++ b/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js @@ -39,7 +39,8 @@ import { renameCollection as _renameCollection, removeCollection as _removeCollection, sortCollections as _sortCollections, - collectionAddEnvFileEvent as _collectionAddEnvFileEvent + collectionAddEnvFileEvent as _collectionAddEnvFileEvent, + updateNewRequest } from './index'; import { closeAllCollectionTabs } from 'providers/ReduxStore/slices/tabs'; @@ -595,6 +596,8 @@ export const newHttpRequest = (params) => (dispatch, getState) => { const { ipcRenderer } = window; ipcRenderer.invoke('renderer:new-request', fullName, item).then(resolve).catch(reject); + // Add the new request name here so it can be opened in a new tab in useCollectionTreeSync.js + dispatch(updateNewRequest({ newRequestName: item.name })) } else { return reject(new Error('Duplicate request names are not allowed under the same folder')); } @@ -612,6 +615,8 @@ export const newHttpRequest = (params) => (dispatch, getState) => { const { ipcRenderer } = window; ipcRenderer.invoke('renderer:new-request', fullName, item).then(resolve).catch(reject); + // Add the new request name here so it can be opened in a new tab in useCollectionTreeSync.js + dispatch(updateNewRequest({ newRequestName: item.name })) } else { return reject(new Error('Duplicate request names are not allowed under the same folder')); } diff --git a/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js b/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js index 2cb1bdea5..d7ff34391 100644 --- a/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js +++ b/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js @@ -29,7 +29,8 @@ const PATH_SEPARATOR = path.sep; const initialState = { collections: [], - collectionSortOrder: 'default' + collectionSortOrder: 'default', + newRequestName: null }; export const collectionsSlice = createSlice({ @@ -129,6 +130,10 @@ export const collectionsSlice = createSlice({ } } }, + updateNewRequest: (state, action) => { + const { newRequestName } = action.payload; + state.newRequestName = newRequestName; + }, newItem: (state, action) => { const collection = findCollectionByUid(state.collections, action.payload.collectionUid); @@ -1200,6 +1205,7 @@ export const { collectionUnlinkEnvFileEvent, saveEnvironment, selectEnvironment, + updateNewRequest, newItem, deleteItem, renameItem, From 57e0f0c0c4fb7505ccd505cd1e6a7ec9544a6f7b Mon Sep 17 00:00:00 2001 From: "Peter C." Date: Thu, 5 Oct 2023 09:45:08 +0200 Subject: [PATCH 04/23] updated insomnia importer --- .../utils/importers/insomnia-collection.js | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/packages/bruno-app/src/utils/importers/insomnia-collection.js b/packages/bruno-app/src/utils/importers/insomnia-collection.js index b402e8903..8b7b7fb6a 100644 --- a/packages/bruno-app/src/utils/importers/insomnia-collection.js +++ b/packages/bruno-app/src/utils/importers/insomnia-collection.js @@ -41,6 +41,16 @@ const addSuffixToDuplicateName = (item, index, allItems) => { return nameSuffix !== 0 ? `${item.name}_${nameSuffix}` : item.name; }; +const regexVariable = new RegExp('{{.*?}}', 'g'); + +const normalizeVariables = (value) => { + const variables = value.match(regexVariable) || []; + each(variables, (variable) => { + value = value.replace(variable, variable.replace('_.', '').replaceAll(' ', '')); + }); + return value; +}; + const transformInsomniaRequestItem = (request, index, allRequests) => { const name = addSuffixToDuplicateName(request, index, allRequests); @@ -51,6 +61,11 @@ const transformInsomniaRequestItem = (request, index, allRequests) => { request: { url: request.url, method: request.method, + auth: { + mode: 'none', + basic: null, + bearer: null + }, headers: [], params: [], body: { @@ -84,7 +99,22 @@ const transformInsomniaRequestItem = (request, index, allRequests) => { }); }); - const mimeType = get(request, 'body.mimeType', ''); + const authType = get(request, 'authentication.type', ''); + + if (authType === 'basic') { + brunoRequestItem.request.auth.mode = 'basic'; + brunoRequestItem.request.auth.basic = { + username: normalizeVariables(get(request, 'authentication.username', '')), + password: normalizeVariables(get(request, 'authentication.password', '')) + }; + } else if (authType === 'bearer') { + brunoRequestItem.request.auth.mode = 'bearer'; + brunoRequestItem.request.auth.bearer = { + token: normalizeVariables(get(request, 'authentication.token', '')) + }; + } + + const mimeType = get(request, 'body.mimeType', '').split(';')[0]; if (mimeType === 'application/json') { brunoRequestItem.request.body.mode = 'json'; From 960f62ac8ed9e95c572421e03e4b8d731438d35c Mon Sep 17 00:00:00 2001 From: Anoop M D Date: Thu, 5 Oct 2023 15:44:34 +0530 Subject: [PATCH 05/23] chore: updated readme --- packages/bruno-electron/package.json | 2 +- readme.md | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/bruno-electron/package.json b/packages/bruno-electron/package.json index c9d6c213c..c57c8176a 100644 --- a/packages/bruno-electron/package.json +++ b/packages/bruno-electron/package.json @@ -9,7 +9,7 @@ "scripts": { "clean": "rimraf dist", "dev": "electron .", - "dist": "electron-builder --mac --config electron-builder-config.js", + "dist": "electron-builder --win --config electron-builder-config.js", "pack": "electron-builder --dir", "test": "jest" }, diff --git a/readme.md b/readme.md index 82d701270..9dc024c83 100644 --- a/readme.md +++ b/readme.md @@ -30,13 +30,14 @@ Or any version control system of your choice ![bruno](assets/images/version-control.png)

-### Website 📄 +### Important Links 📌 -Please visit [here](https://www.usebruno.com) to checkout our website and download the app - -### Documentation 📄 - -Please visit [here](https://docs.usebruno.com) for documentation +- [Our Long Term Vision](https://github.com/usebruno/bruno/discussions/269) +- [Roadmap](https://github.com/usebruno/bruno/discussions/384) +- [Share Testimonials](https://github.com/usebruno/bruno/discussions/343) +- [Documentation](https://docs.usebruno.com) +- [Website](https://www.usebruno.com) +- [Download](https://www.usebruno.com/downloads) ### Support ❤️ From 99fc980a48d1cbb4528c6a673b740f72f9f4f78a Mon Sep 17 00:00:00 2001 From: Anoop M D Date: Thu, 5 Oct 2023 19:30:38 +0530 Subject: [PATCH 06/23] chore: updated readme --- readme.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 9dc024c83..651d8cebe 100644 --- a/readme.md +++ b/readme.md @@ -34,11 +34,16 @@ Or any version control system of your choice - [Our Long Term Vision](https://github.com/usebruno/bruno/discussions/269) - [Roadmap](https://github.com/usebruno/bruno/discussions/384) -- [Share Testimonials](https://github.com/usebruno/bruno/discussions/343) - [Documentation](https://docs.usebruno.com) - [Website](https://www.usebruno.com) - [Download](https://www.usebruno.com/downloads) +### Showcase 🎥 + +- [Testimonials](https://github.com/usebruno/bruno/discussions/343) +- [Knowledge Hub](https://github.com/usebruno/bruno/discussions/386) +- [Scriptmania](https://github.com/usebruno/bruno/discussions/385) + ### Support ❤️ Woof! If you like project, hit that ⭐ button !! From 0cbf803ed9c79b637100c60a5dc327b3456dbfee Mon Sep 17 00:00:00 2001 From: Anoop M D Date: Thu, 5 Oct 2023 19:51:06 +0530 Subject: [PATCH 07/23] chore: restructured tests into seperate dir as jest error was preventing local exec of cli --- .../{src/commands/run.test.js => tests/commands/run.spec.js} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename packages/bruno-cli/{src/commands/run.test.js => tests/commands/run.spec.js} (97%) diff --git a/packages/bruno-cli/src/commands/run.test.js b/packages/bruno-cli/tests/commands/run.spec.js similarity index 97% rename from packages/bruno-cli/src/commands/run.test.js rename to packages/bruno-cli/tests/commands/run.spec.js index 979aec5a3..10cdf42b4 100644 --- a/packages/bruno-cli/src/commands/run.test.js +++ b/packages/bruno-cli/tests/commands/run.spec.js @@ -1,6 +1,6 @@ const { describe, it, expect } = require('@jest/globals'); -const { printRunSummary } = require('./run'); +const { printRunSummary } = require('../../src/commands/run'); describe('printRunSummary', () => { // Suppress console.log output From 04481a26e19917f18c9550a25dcab4a5802ad1f6 Mon Sep 17 00:00:00 2001 From: Stefan Ollinger Date: Thu, 5 Oct 2023 16:36:57 +0200 Subject: [PATCH 08/23] Show response time in milliseconds per request and total --- packages/bruno-cli/src/commands/run.js | 3 ++- .../bruno-cli/src/runner/run-single-request.js | 16 ++++++++++++---- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/packages/bruno-cli/src/commands/run.js b/packages/bruno-cli/src/commands/run.js index ff0ab42ea..b7bb0d627 100644 --- a/packages/bruno-cli/src/commands/run.js +++ b/packages/bruno-cli/src/commands/run.js @@ -356,7 +356,8 @@ const handler = async function (argv) { } const summary = printRunSummary(results); - console.log(chalk.dim(chalk.grey('Ran all requests.'))); + 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); diff --git a/packages/bruno-cli/src/runner/run-single-request.js b/packages/bruno-cli/src/runner/run-single-request.js index 79435785e..69ecb1fad 100644 --- a/packages/bruno-cli/src/runner/run-single-request.js +++ b/packages/bruno-cli/src/runner/run-single-request.js @@ -122,13 +122,16 @@ const runSingleRequest = async function ( request.data = qs.stringify(request.data); } - let response; + let response, responseTime; try { // run request + const start = Date.now(); response = await axios(request); + responseTime = Date.now() - start; } catch (err) { if (err && err.response) { response = err.response; + responseTime = Date.now() - start; } else { console.log(chalk.red(stripExtension(filename)) + chalk.dim(` (${err.message})`)); return { @@ -142,7 +145,8 @@ const runSingleRequest = async function ( status: null, statusText: null, headers: null, - data: null + data: null, + responseTime: 0 }, error: err.message, assertionResults: [], @@ -151,7 +155,10 @@ const runSingleRequest = async function ( } } - console.log(chalk.green(stripExtension(filename)) + chalk.dim(` (${response.status} ${response.statusText})`)); + 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'); @@ -247,7 +254,8 @@ const runSingleRequest = async function ( status: response.status, statusText: response.statusText, headers: response.headers, - data: response.data + data: response.data, + responseTime }, error: null, assertionResults, From af130bac17fb52851d04838fa7fd10c9cd9fa85c Mon Sep 17 00:00:00 2001 From: Jonathan Gruber Date: Thu, 5 Oct 2023 16:35:22 +0200 Subject: [PATCH 09/23] feat(usebruno#354): Add authentication for GraphQL requests Schema introspection query will also use specified authentication. --- .../RequestPane/GraphQLRequestPane/index.js | 16 ++++++++++++++- .../GraphQLRequestPane/useGraphqlSchema.js | 4 ++-- packages/bruno-app/src/utils/network/index.js | 7 +++++-- .../bruno-electron/src/ipc/network/index.js | 8 +++++--- .../prepare-gql-introspection-request.js | 20 ++++++++++++++++--- 5 files changed, 44 insertions(+), 11 deletions(-) diff --git a/packages/bruno-app/src/components/RequestPane/GraphQLRequestPane/index.js b/packages/bruno-app/src/components/RequestPane/GraphQLRequestPane/index.js index 773d8011c..45a345a6a 100644 --- a/packages/bruno-app/src/components/RequestPane/GraphQLRequestPane/index.js +++ b/packages/bruno-app/src/components/RequestPane/GraphQLRequestPane/index.js @@ -6,6 +6,7 @@ import { IconRefresh, IconLoader2, IconBook, IconDownload } from '@tabler/icons' import { useSelector, useDispatch } from 'react-redux'; import { updateRequestPaneTab } from 'providers/ReduxStore/slices/tabs'; import QueryEditor from 'components/RequestPane/QueryEditor'; +import Auth from 'components/RequestPane/Auth'; import GraphQLVariables from 'components/RequestPane/GraphQLVariables'; import RequestHeaders from 'components/RequestPane/RequestHeaders'; import Vars from 'components/RequestPane/Vars'; @@ -32,7 +33,14 @@ const GraphQLRequestPane = ({ item, collection, leftPaneWidth, onSchemaLoad, tog const environment = findEnvironmentInCollection(collection, collection.activeEnvironmentUid); - let { schema, loadSchema, isLoading: isSchemaLoading, error: schemaError } = useGraphqlSchema(url, environment); + const request = item.draft ? item.draft.request : item.request; + + let { + schema, + loadSchema, + isLoading: isSchemaLoading, + error: schemaError + } = useGraphqlSchema(url, environment, request, collection.collectionVariables); const loadGqlSchema = () => { if (!isSchemaLoading) { @@ -90,6 +98,9 @@ const GraphQLRequestPane = ({ item, collection, leftPaneWidth, onSchemaLoad, tog case 'headers': { return ; } + case 'auth': { + return ; + } case 'vars': { return ; } @@ -135,6 +146,9 @@ const GraphQLRequestPane = ({ item, collection, leftPaneWidth, onSchemaLoad, tog
selectTab('headers')}> Headers
+
selectTab('auth')}> + Auth +
selectTab('vars')}> Vars
diff --git a/packages/bruno-app/src/components/RequestPane/GraphQLRequestPane/useGraphqlSchema.js b/packages/bruno-app/src/components/RequestPane/GraphQLRequestPane/useGraphqlSchema.js index 7cfe7f951..e59127717 100644 --- a/packages/bruno-app/src/components/RequestPane/GraphQLRequestPane/useGraphqlSchema.js +++ b/packages/bruno-app/src/components/RequestPane/GraphQLRequestPane/useGraphqlSchema.js @@ -6,7 +6,7 @@ import { simpleHash } from 'utils/common'; const schemaHashPrefix = 'bruno.graphqlSchema'; -const useGraphqlSchema = (endpoint, environment) => { +const useGraphqlSchema = (endpoint, environment, request, collectionVariables) => { const localStorageKey = `${schemaHashPrefix}.${simpleHash(endpoint)}`; const [error, setError] = useState(null); const [isLoading, setIsLoading] = useState(false); @@ -25,7 +25,7 @@ const useGraphqlSchema = (endpoint, environment) => { const loadSchema = () => { setIsLoading(true); - fetchGqlSchema(endpoint, environment) + fetchGqlSchema(endpoint, environment, request, collectionVariables) .then((res) => res.data) .then((s) => { if (s && s.data) { diff --git a/packages/bruno-app/src/utils/network/index.js b/packages/bruno-app/src/utils/network/index.js index f4af70926..2c5c3a1d3 100644 --- a/packages/bruno-app/src/utils/network/index.js +++ b/packages/bruno-app/src/utils/network/index.js @@ -29,11 +29,14 @@ const sendHttpRequest = async (item, collection, environment, collectionVariable }); }; -export const fetchGqlSchema = async (endpoint, environment) => { +export const fetchGqlSchema = async (endpoint, environment, request, collectionVariables) => { return new Promise((resolve, reject) => { const { ipcRenderer } = window; - ipcRenderer.invoke('fetch-gql-schema', endpoint, environment).then(resolve).catch(reject); + ipcRenderer + .invoke('fetch-gql-schema', endpoint, environment, request, collectionVariables) + .then(resolve) + .catch(reject); }); }; diff --git a/packages/bruno-electron/src/ipc/network/index.js b/packages/bruno-electron/src/ipc/network/index.js index c3ade9c05..89740b8bc 100644 --- a/packages/bruno-electron/src/ipc/network/index.js +++ b/packages/bruno-electron/src/ipc/network/index.js @@ -458,10 +458,10 @@ const registerNetworkIpc = (mainWindow) => { }); }); - ipcMain.handle('fetch-gql-schema', async (event, endpoint, environment) => { + ipcMain.handle('fetch-gql-schema', async (event, endpoint, environment, request, collectionVariables) => { try { const envVars = getEnvVars(environment); - const request = prepareGqlIntrospectionRequest(endpoint, envVars); + const preparedRequest = prepareGqlIntrospectionRequest(endpoint, envVars, request); const preferences = getPreferences(); const sslVerification = get(preferences, 'request.sslVerification', true); @@ -472,7 +472,9 @@ const registerNetworkIpc = (mainWindow) => { }); } - const response = await axios(request); + interpolateVars(preparedRequest, envVars, collectionVariables); + + const response = await axios(preparedRequest); return { status: response.status, diff --git a/packages/bruno-electron/src/ipc/network/prepare-gql-introspection-request.js b/packages/bruno-electron/src/ipc/network/prepare-gql-introspection-request.js index a36666e31..d41be8f4c 100644 --- a/packages/bruno-electron/src/ipc/network/prepare-gql-introspection-request.js +++ b/packages/bruno-electron/src/ipc/network/prepare-gql-introspection-request.js @@ -1,12 +1,13 @@ const Mustache = require('mustache'); const { getIntrospectionQuery } = require('graphql'); +const { get } = require('lodash'); // override the default escape function to prevent escaping Mustache.escape = function (value) { return value; }; -const prepareGqlIntrospectionRequest = (endpoint, envVars) => { +const prepareGqlIntrospectionRequest = (endpoint, envVars, request) => { if (endpoint && endpoint.length) { endpoint = Mustache.render(endpoint, envVars); } @@ -15,7 +16,7 @@ const prepareGqlIntrospectionRequest = (endpoint, envVars) => { query: introspectionQuery }; - const request = { + let axiosRequest = { method: 'POST', url: endpoint, headers: { @@ -25,7 +26,20 @@ const prepareGqlIntrospectionRequest = (endpoint, envVars) => { data: JSON.stringify(queryParams) }; - return request; + if (request.auth) { + if (request.auth.mode === 'basic') { + axiosRequest.auth = { + username: get(request, 'auth.basic.username'), + password: get(request, 'auth.basic.password') + }; + } + + if (request.auth.mode === 'bearer') { + axiosRequest.headers.authorization = `Bearer ${get(request, 'auth.bearer.token')}`; + } + } + + return axiosRequest; }; module.exports = prepareGqlIntrospectionRequest; From e90718f47bf90084b9017662d62b1ea2ecb6cc2e Mon Sep 17 00:00:00 2001 From: Stefan Ollinger Date: Thu, 5 Oct 2023 16:42:49 +0200 Subject: [PATCH 10/23] Show response time in milliseconds per request and total --- packages/bruno-cli/src/commands/run.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/bruno-cli/src/commands/run.js b/packages/bruno-cli/src/commands/run.js index b7bb0d627..7866425ec 100644 --- a/packages/bruno-cli/src/commands/run.js +++ b/packages/bruno-cli/src/commands/run.js @@ -357,7 +357,7 @@ const handler = async function (argv) { 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`))); + console.log(chalk.dim(chalk.grey(`Ran all requests - ${totalTime} ms`))); if (outputPath && outputPath.length) { const outputDir = path.dirname(outputPath); From f9f99adbf983de1f17a344ddac54a4296233265c Mon Sep 17 00:00:00 2001 From: zyrouge Date: Thu, 5 Oct 2023 20:23:33 +0530 Subject: [PATCH 11/23] refactor: set icon of windows --- packages/bruno-electron/src/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/bruno-electron/src/index.js b/packages/bruno-electron/src/index.js index fb7fc45b4..12cfde8b6 100644 --- a/packages/bruno-electron/src/index.js +++ b/packages/bruno-electron/src/index.js @@ -35,7 +35,8 @@ app.on('ready', async () => { contextIsolation: true, preload: path.join(__dirname, 'preload.js'), webviewTag: true - } + }, + icon: path.join(__dirname, 'about/256x256.png') }); const url = isDev From 8703124007ee3c4206d6ea660e788a333022a4f4 Mon Sep 17 00:00:00 2001 From: zyrouge Date: Thu, 5 Oct 2023 20:24:02 +0530 Subject: [PATCH 12/23] refactor: enable auto hide menu bar --- packages/bruno-electron/src/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/bruno-electron/src/index.js b/packages/bruno-electron/src/index.js index 12cfde8b6..a9697f4ea 100644 --- a/packages/bruno-electron/src/index.js +++ b/packages/bruno-electron/src/index.js @@ -36,7 +36,8 @@ app.on('ready', async () => { preload: path.join(__dirname, 'preload.js'), webviewTag: true }, - icon: path.join(__dirname, 'about/256x256.png') + icon: path.join(__dirname, 'about/256x256.png'), + autoHideMenuBar: true }); const url = isDev From 4f0d87fba4f1d01eac7c5b63f16b62dac280af91 Mon Sep 17 00:00:00 2001 From: zyrouge Date: Thu, 5 Oct 2023 20:29:50 +0530 Subject: [PATCH 13/23] refactor: set title --- packages/bruno-electron/src/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/bruno-electron/src/index.js b/packages/bruno-electron/src/index.js index a9697f4ea..3908373e9 100644 --- a/packages/bruno-electron/src/index.js +++ b/packages/bruno-electron/src/index.js @@ -36,6 +36,7 @@ app.on('ready', async () => { preload: path.join(__dirname, 'preload.js'), webviewTag: true }, + title: 'Bruno', icon: path.join(__dirname, 'about/256x256.png'), autoHideMenuBar: true }); From ae2f170fe6affdd6ae1c69082fd129546977a2bc Mon Sep 17 00:00:00 2001 From: Stefan Ollinger Date: Thu, 5 Oct 2023 17:05:31 +0200 Subject: [PATCH 14/23] Put constant in outer scope --- packages/bruno-cli/src/runner/run-single-request.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/bruno-cli/src/runner/run-single-request.js b/packages/bruno-cli/src/runner/run-single-request.js index 69ecb1fad..6cce5435d 100644 --- a/packages/bruno-cli/src/runner/run-single-request.js +++ b/packages/bruno-cli/src/runner/run-single-request.js @@ -123,9 +123,9 @@ const runSingleRequest = async function ( } let response, responseTime; + const start = Date.now(); try { // run request - const start = Date.now(); response = await axios(request); responseTime = Date.now() - start; } catch (err) { From efbdde8252601f72c01c06504768e27e60931f40 Mon Sep 17 00:00:00 2001 From: Stefan Ollinger Date: Thu, 5 Oct 2023 17:11:29 +0200 Subject: [PATCH 15/23] Set `responseTime` --- packages/bruno-cli/src/runner/run-single-request.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/bruno-cli/src/runner/run-single-request.js b/packages/bruno-cli/src/runner/run-single-request.js index 6cce5435d..e5c42392a 100644 --- a/packages/bruno-cli/src/runner/run-single-request.js +++ b/packages/bruno-cli/src/runner/run-single-request.js @@ -273,7 +273,8 @@ const runSingleRequest = async function ( status: null, statusText: null, headers: null, - data: null + data: null, + responseTime: 0 }, error: err.message, assertionResults: [], From 8a19189dcd997d72f28f8d63d04eba493f46783d Mon Sep 17 00:00:00 2001 From: Its-treason <39559178+Its-treason@users.noreply.github.com> Date: Thu, 5 Oct 2023 17:43:17 +0200 Subject: [PATCH 16/23] feat(#111): Use existing last actions for auto open tabs --- .../providers/App/useCollectionTreeSync.js | 37 ++++++++++--------- .../ReduxStore/slices/collections/actions.js | 4 +- .../ReduxStore/slices/collections/index.js | 7 +--- 3 files changed, 23 insertions(+), 25 deletions(-) diff --git a/packages/bruno-app/src/providers/App/useCollectionTreeSync.js b/packages/bruno-app/src/providers/App/useCollectionTreeSync.js index 89fdaabb5..ba7193f31 100644 --- a/packages/bruno-app/src/providers/App/useCollectionTreeSync.js +++ b/packages/bruno-app/src/providers/App/useCollectionTreeSync.js @@ -17,15 +17,15 @@ import { import toast from 'react-hot-toast'; import { openCollectionEvent, collectionAddEnvFileEvent } from 'providers/ReduxStore/slices/collections/actions'; import { isElectron } from 'utils/common/platform'; -import { updateNewRequest } from 'providers/ReduxStore/slices/collections/index'; import { addTab } from 'providers/ReduxStore/slices/tabs'; -import { getDefaultRequestPaneTab } from 'utils/collections/index'; +import { findCollectionByUid, getDefaultRequestPaneTab } from 'utils/collections/index'; import { hideHomePage } from 'providers/ReduxStore/slices/app'; +import { updateLastAction } from 'providers/ReduxStore/slices/collections/index'; const useCollectionTreeSync = () => { const dispatch = useDispatch(); const tabs = useSelector((state) => state.tabs.tabs); - const newRequestName = useSelector((state) => state.collections.newRequestName); + const collections = useSelector((state) => state.collections.collections); useEffect(() => { if (!isElectron()) { @@ -57,20 +57,23 @@ const useCollectionTreeSync = () => { }) ); - // Remove the newRequestName so no random stuff happens - dispatch( - updateNewRequest({ newRequestName: null }) - ); + const collectionUid = val.meta.collectionUid; + const lastAction = findCollectionByUid(collections, collectionUid)?.lastAction; + // When the request was just created open it in a new tab - if (newRequestName === val.data.name) { - dispatch( - addTab({ - uid: val.data.uid, - collectionUid: val.meta.collectionUid, - requestPaneTab: getDefaultRequestPaneTab(val.data) - }) - ); - dispatch(hideHomePage()); + if (lastAction && lastAction.type === 'ADD_REQUEST') { + dispatch(updateLastAction({ lastAction: null, collectionUid })); + + if (lastAction.payload === val.data.name) { + dispatch( + addTab({ + uid: val.data.uid, + collectionUid: collectionUid, + requestPaneTab: getDefaultRequestPaneTab(val.data) + }) + ); + dispatch(hideHomePage()); + } } } if (type === 'change') { @@ -164,7 +167,7 @@ const useCollectionTreeSync = () => { removeListener10(); removeListener11(); }; - }, [isElectron, tabs, newRequestName]); + }, [isElectron, tabs, collections]); useEffect(() => { if (!isElectron()) { diff --git a/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js b/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js index 3ad39e676..eecb75263 100644 --- a/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js +++ b/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js @@ -597,7 +597,7 @@ export const newHttpRequest = (params) => (dispatch, getState) => { ipcRenderer.invoke('renderer:new-request', fullName, item).then(resolve).catch(reject); // Add the new request name here so it can be opened in a new tab in useCollectionTreeSync.js - dispatch(updateNewRequest({ newRequestName: item.name })) + dispatch(updateLastAction({ lastAction: { type: 'ADD_REQUEST', payload: item.name }, collectionUid })); } else { return reject(new Error('Duplicate request names are not allowed under the same folder')); } @@ -616,7 +616,7 @@ export const newHttpRequest = (params) => (dispatch, getState) => { ipcRenderer.invoke('renderer:new-request', fullName, item).then(resolve).catch(reject); // Add the new request name here so it can be opened in a new tab in useCollectionTreeSync.js - dispatch(updateNewRequest({ newRequestName: item.name })) + dispatch(updateLastAction({ lastAction: { type: 'ADD_REQUEST', payload: item.name }, collectionUid })); } else { return reject(new Error('Duplicate request names are not allowed under the same folder')); } diff --git a/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js b/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js index d7ff34391..ec89bb85d 100644 --- a/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js +++ b/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js @@ -29,8 +29,7 @@ const PATH_SEPARATOR = path.sep; const initialState = { collections: [], - collectionSortOrder: 'default', - newRequestName: null + collectionSortOrder: 'default' }; export const collectionsSlice = createSlice({ @@ -130,10 +129,6 @@ export const collectionsSlice = createSlice({ } } }, - updateNewRequest: (state, action) => { - const { newRequestName } = action.payload; - state.newRequestName = newRequestName; - }, newItem: (state, action) => { const collection = findCollectionByUid(state.collections, action.payload.collectionUid); From 90f47e58777f6bcac90426b7252746a2f977d250 Mon Sep 17 00:00:00 2001 From: Anoop M D Date: Thu, 5 Oct 2023 21:15:38 +0530 Subject: [PATCH 17/23] feat(#396): decomment support in json and scripts --- package-lock.json | 36 +++++++++++++++---- packages/bruno-cli/package.json | 1 + .../bruno-cli/src/runner/prepare-request.js | 5 +-- .../src/runner/run-single-request.js | 7 ++-- packages/bruno-electron/package.json | 1 + .../bruno-electron/src/ipc/network/index.js | 17 ++++----- .../src/ipc/network/prepare-request.js | 6 ++-- 7 files changed, 52 insertions(+), 21 deletions(-) diff --git a/package-lock.json b/package-lock.json index c609f0d84..c22296288 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6678,6 +6678,18 @@ "node": ">=0.10.0" } }, + "node_modules/decomment": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/decomment/-/decomment-0.9.5.tgz", + "integrity": "sha512-h0TZ8t6Dp49duwyDHo3iw67mnh9/UpFiSSiOb5gDK1sqoXzrfX/SQxIUQd2R2QEiSnqib0KF2fnKnGfAhAs6lg==", + "dependencies": { + "esprima": "4.0.1" + }, + "engines": { + "node": ">=6.4", + "npm": ">=2.15" + } + }, "node_modules/decompress-response": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", @@ -7505,7 +7517,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, "bin": { "esparse": "bin/esparse.js", "esvalidate": "bin/esvalidate.js" @@ -16765,7 +16776,7 @@ }, "packages/bruno-cli": { "name": "@usebruno/cli", - "version": "0.10.1", + "version": "0.11.0", "license": "MIT", "dependencies": { "@usebruno/js": "0.6.0", @@ -16773,6 +16784,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", @@ -16810,7 +16822,7 @@ }, "packages/bruno-electron": { "name": "bruno", - "version": "v0.18.0", + "version": "v0.19.0", "dependencies": { "@usebruno/js": "0.6.0", "@usebruno/lang": "0.5.0", @@ -16819,6 +16831,7 @@ "axios": "^1.5.1", "chai": "^4.3.7", "chokidar": "^3.5.3", + "decomment": "^0.9.5", "dotenv": "^16.0.3", "electron-is-dev": "^2.0.0", "electron-notarize": "^1.2.2", @@ -16848,7 +16861,9 @@ "dmg-license": "^1.0.11" } }, - "packages/bruno-electron/0.5cd ../.0": {}, + "packages/bruno-electron/0.5cd ../.0": { + "extraneous": true + }, "packages/bruno-electron/node_modules/@types/node": { "version": "16.18.11", "dev": true, @@ -20092,6 +20107,7 @@ "axios": "^1.5.1", "chai": "^4.3.7", "chalk": "^3.0.0", + "decomment": "*", "form-data": "^4.0.0", "fs-extra": "^10.1.0", "handlebars": "^4.7.8", @@ -21201,6 +21217,7 @@ "axios": "^1.5.1", "chai": "^4.3.7", "chokidar": "^3.5.3", + "decomment": "^0.9.5", "dmg-license": "^1.0.11", "dotenv": "^16.0.3", "electron": "21.1.1", @@ -22309,6 +22326,14 @@ "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", "dev": true }, + "decomment": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/decomment/-/decomment-0.9.5.tgz", + "integrity": "sha512-h0TZ8t6Dp49duwyDHo3iw67mnh9/UpFiSSiOb5gDK1sqoXzrfX/SQxIUQd2R2QEiSnqib0KF2fnKnGfAhAs6lg==", + "requires": { + "esprima": "4.0.1" + } + }, "decompress-response": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", @@ -22958,8 +22983,7 @@ "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" }, "esrecurse": { "version": "4.3.0", diff --git a/packages/bruno-cli/package.json b/packages/bruno-cli/package.json index abb99627b..326ad8fc4 100644 --- a/packages/bruno-cli/package.json +++ b/packages/bruno-cli/package.json @@ -29,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", diff --git a/packages/bruno-cli/src/runner/prepare-request.js b/packages/bruno-cli/src/runner/prepare-request.js index e766d08ea..e52cb5418 100644 --- a/packages/bruno-cli/src/runner/prepare-request.js +++ b/packages/bruno-cli/src/runner/prepare-request.js @@ -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'; diff --git a/packages/bruno-cli/src/runner/run-single-request.js b/packages/bruno-cli/src/runner/run-single-request.js index 69ecb1fad..78985079b 100644 --- a/packages/bruno-cli/src/runner/run-single-request.js +++ b/packages/bruno-cli/src/runner/run-single-request.js @@ -1,5 +1,6 @@ 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'); @@ -55,7 +56,7 @@ const runSingleRequest = async function ( if (requestScriptFile && requestScriptFile.length) { const scriptRuntime = new ScriptRuntime(); await scriptRuntime.runRequestScript( - requestScriptFile, + decomment(requestScriptFile), request, envVariables, collectionVariables, @@ -180,7 +181,7 @@ const runSingleRequest = async function ( if (responseScriptFile && responseScriptFile.length) { const scriptRuntime = new ScriptRuntime(); await scriptRuntime.runResponseScript( - responseScriptFile, + decomment(responseScriptFile), request, response, envVariables, @@ -221,7 +222,7 @@ const runSingleRequest = async function ( if (typeof testFile === 'string') { const testRuntime = new TestRuntime(); const result = await testRuntime.runTests( - testFile, + decomment(testFile), request, response, envVariables, diff --git a/packages/bruno-electron/package.json b/packages/bruno-electron/package.json index c57c8176a..6c81f5dc5 100644 --- a/packages/bruno-electron/package.json +++ b/packages/bruno-electron/package.json @@ -21,6 +21,7 @@ "axios": "^1.5.1", "chai": "^4.3.7", "chokidar": "^3.5.3", + "decomment": "^0.9.5", "dotenv": "^16.0.3", "electron-is-dev": "^2.0.0", "electron-notarize": "^1.2.2", diff --git a/packages/bruno-electron/src/ipc/network/index.js b/packages/bruno-electron/src/ipc/network/index.js index 89740b8bc..2dad72860 100644 --- a/packages/bruno-electron/src/ipc/network/index.js +++ b/packages/bruno-electron/src/ipc/network/index.js @@ -1,6 +1,7 @@ const qs = require('qs'); const https = require('https'); const axios = require('axios'); +const decomment = require('decomment'); const Mustache = require('mustache'); const FormData = require('form-data'); const { ipcMain } = require('electron'); @@ -151,7 +152,7 @@ const registerNetworkIpc = (mainWindow) => { if (requestScript && requestScript.length) { const scriptRuntime = new ScriptRuntime(); const result = await scriptRuntime.runRequestScript( - requestScript, + decomment(requestScript), request, envVars, collectionVariables, @@ -277,7 +278,7 @@ const registerNetworkIpc = (mainWindow) => { if (responseScript && responseScript.length) { const scriptRuntime = new ScriptRuntime(); const result = await scriptRuntime.runResponseScript( - responseScript, + decomment(responseScript), request, response, envVars, @@ -323,7 +324,7 @@ const registerNetworkIpc = (mainWindow) => { if (typeof testFile === 'string') { const testRuntime = new TestRuntime(); const testResults = await testRuntime.runTests( - testFile, + decomment(testFile), request, response, envVars, @@ -402,7 +403,7 @@ const registerNetworkIpc = (mainWindow) => { if (typeof testFile === 'string') { const testRuntime = new TestRuntime(); const testResults = await testRuntime.runTests( - testFile, + decomment(testFile), request, error.response, envVars, @@ -603,7 +604,7 @@ const registerNetworkIpc = (mainWindow) => { if (requestScript && requestScript.length) { const scriptRuntime = new ScriptRuntime(); const result = await scriptRuntime.runRequestScript( - requestScript, + decomment(requestScript), request, envVars, collectionVariables, @@ -704,7 +705,7 @@ const registerNetworkIpc = (mainWindow) => { if (responseScript && responseScript.length) { const scriptRuntime = new ScriptRuntime(); const result = await scriptRuntime.runResponseScript( - responseScript, + decomment(responseScript), request, response, envVars, @@ -748,7 +749,7 @@ const registerNetworkIpc = (mainWindow) => { if (typeof testFile === 'string') { const testRuntime = new TestRuntime(); const testResults = await testRuntime.runTests( - testFile, + decomment(testFile), request, response, envVars, @@ -828,7 +829,7 @@ const registerNetworkIpc = (mainWindow) => { if (typeof testFile === 'string') { const testRuntime = new TestRuntime(); const testResults = await testRuntime.runTests( - testFile, + decomment(testFile), request, error.response, envVars, diff --git a/packages/bruno-electron/src/ipc/network/prepare-request.js b/packages/bruno-electron/src/ipc/network/prepare-request.js index 5a8512910..922c9929e 100644 --- a/packages/bruno-electron/src/ipc/network/prepare-request.js +++ b/packages/bruno-electron/src/ipc/network/prepare-request.js @@ -1,4 +1,5 @@ const { get, each, filter } = require('lodash'); +const decomment = require('decomment'); const prepareRequest = (request) => { const headers = {}; @@ -37,7 +38,8 @@ const prepareRequest = (request) => { axiosRequest.headers['content-type'] = 'application/json'; } try { - axiosRequest.data = JSON.parse(request.body.json); + // axiosRequest.data = JSON.parse(request.body.json); + axiosRequest.data = JSON.parse(decomment(request.body.json)); } catch (ex) { axiosRequest.data = request.body.json; } @@ -76,7 +78,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'; From bb7d13d2d969feaafbe5c75299c570962db53b39 Mon Sep 17 00:00:00 2001 From: Stefan Ollinger Date: Thu, 5 Oct 2023 17:46:12 +0200 Subject: [PATCH 18/23] Use axios interceptor to measure response time --- .../src/runner/run-single-request.js | 22 ++++++---- .../bruno-cli/src/utils/axios-instance.js | 40 +++++++++++++++++++ 2 files changed, 54 insertions(+), 8 deletions(-) create mode 100644 packages/bruno-cli/src/utils/axios-instance.js diff --git a/packages/bruno-cli/src/runner/run-single-request.js b/packages/bruno-cli/src/runner/run-single-request.js index e5c42392a..7323d4091 100644 --- a/packages/bruno-cli/src/runner/run-single-request.js +++ b/packages/bruno-cli/src/runner/run-single-request.js @@ -10,6 +10,7 @@ const interpolateVars = require('./interpolate-vars'); const { ScriptRuntime, TestRuntime, VarsRuntime, AssertRuntime } = require('@usebruno/js'); const { stripExtension } = require('../utils/filesystem'); const { getOptions } = require('../utils/bru'); +const { makeAxiosInstance } = require('../utils/axios-instance'); const runSingleRequest = async function ( filename, @@ -123,15 +124,23 @@ const runSingleRequest = async function ( } let response, responseTime; - const start = Date.now(); try { // run request - response = await axios(request); - responseTime = Date.now() - start; + const axiosInstance = makeAxiosInstance(); + + /** @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; - responseTime = Date.now() - start; + + // 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 { @@ -155,10 +164,7 @@ const runSingleRequest = async function ( } } - console.log( - chalk.green(stripExtension(filename)) + - chalk.dim(` (${response.status} ${response.statusText}) - ${responseTime} ms`) - ); + 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'); diff --git a/packages/bruno-cli/src/utils/axios-instance.js b/packages/bruno-cli/src/utils/axios-instance.js new file mode 100644 index 000000000..f4810becd --- /dev/null +++ b/packages/bruno-cli/src/utils/axios-instance.js @@ -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 +}; From a71afc8f73ed2fd754a4baa72d0dee5d22485f5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Szymborski?= Date: Thu, 5 Oct 2023 19:38:17 +0200 Subject: [PATCH 19/23] fix(#399): loading environment with empty secret variable fails --- packages/bruno-electron/src/app/watcher.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/bruno-electron/src/app/watcher.js b/packages/bruno-electron/src/app/watcher.js index c5973e79c..b4162db90 100644 --- a/packages/bruno-electron/src/app/watcher.js +++ b/packages/bruno-electron/src/app/watcher.js @@ -103,7 +103,7 @@ const addEnvironmentFile = async (win, pathname, collectionUid, collectionPath) const envSecrets = environmentSecretsStore.getEnvSecrets(collectionPath, file.data); _.each(envSecrets, (secret) => { const variable = _.find(file.data.variables, (v) => v.name === secret.name); - if (variable) { + if (variable && secret.value) { variable.value = decryptString(secret.value); } }); @@ -137,7 +137,7 @@ const changeEnvironmentFile = async (win, pathname, collectionUid, collectionPat const envSecrets = environmentSecretsStore.getEnvSecrets(collectionPath, file.data); _.each(envSecrets, (secret) => { const variable = _.find(file.data.variables, (v) => v.name === secret.name); - if (variable) { + if (variable && secret.value) { variable.value = decryptString(secret.value); } }); From 64d9413777369ea38dc4b2f4c5c98de2f4c03969 Mon Sep 17 00:00:00 2001 From: Qweme Dev <99718350+qweme32@users.noreply.github.com> Date: Thu, 5 Oct 2023 22:47:14 +0300 Subject: [PATCH 20/23] Add Russian localization --- contributing.md | 2 ++ contributing_ru.md | 37 ++++++++++++++++++++ docs/development.md | 2 ++ docs/development_ru.md | 55 +++++++++++++++++++++++++++++ readme.md | 4 ++- readme_ru.md | 79 ++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 178 insertions(+), 1 deletion(-) create mode 100644 contributing_ru.md create mode 100644 docs/development_ru.md create mode 100644 readme_ru.md diff --git a/contributing.md b/contributing.md index a538f1bd7..abfcce4d3 100644 --- a/contributing.md +++ b/contributing.md @@ -1,3 +1,5 @@ +**English** | [Русский](/contributing_ru.md) + ## Lets make bruno better, together !! I am happy that you are looking to improve bruno. Below are the guidelines to get started bringing up bruno on your computer. diff --git a/contributing_ru.md b/contributing_ru.md new file mode 100644 index 000000000..316408162 --- /dev/null +++ b/contributing_ru.md @@ -0,0 +1,37 @@ +[English](/contributing.md) | **Русский** + +## Давайте вместе сделаем Бруно лучше!!! + +Я рад, что Вы хотите усовершенствовать bruno. Ниже приведены рекомендации по запуску bruno на вашем компьютере. + +### Стек + +Bruno построен с использованием NextJs и React. Мы также используем electron для поставки десктопной версии ( которая поддерживает локальные коллекции ) + +Библиотеки, которые мы используем + +- CSS - Tailwind +- Редакторы кода - Codemirror +- Управление состоянием - Redux +- Иконки - Tabler Icons +- Формы - formik +- Валидация схем - Yup +- Запросы клиента - axios +- Наблюдатель за файловой системой - chokidar + +### Зависимости + +Вам потребуется [Node v18.x или последняя версия LTS](https://nodejs.org/en/) и npm 8.x. В проекте мы используем рабочие пространства npm + +### Приступим к коду + +Пожалуйста, обратитесь к [development_ru.md](docs/development_ru.md) для получения инструкций по запуску локальной среды разработки. + +### Создание Pull Request + +- Пожалуйста, пусть PR будет небольшим и сфокусированным на одной вещи +- Пожалуйста, соблюдайте формат создания веток + - feature/[название функции]: Эта ветка должна содержать изменения для конкретной функции + - Пример: feature/dark-mode + - bugfix/[название ошибки]: Эта ветка должна содержать только исправления для конкретной ошибки + - Пример bugfix/bug-1 \ No newline at end of file diff --git a/docs/development.md b/docs/development.md index 77614d2f6..c1c402e08 100644 --- a/docs/development.md +++ b/docs/development.md @@ -1,3 +1,5 @@ +**English** | [Русский](/docs/development_ru.md) + ## Development Bruno is being developed as a desktop app. You need to load the app by running the nextjs app in one terminal and then run the electron app in another terminal. diff --git a/docs/development_ru.md b/docs/development_ru.md new file mode 100644 index 000000000..4d4e3a80e --- /dev/null +++ b/docs/development_ru.md @@ -0,0 +1,55 @@ +[English](/docs/development.md) | **Русский** + +## Разработка + +Bruno разрабатывается как десктопное приложение. Необходимо загрузить приложение, запустив приложение nextjs в одном терминале, а затем запустить приложение electron в другом терминале. + +### Зависимости + +- NodeJS v18 + +### Локальная разработка + +```bash +# используйте nodejs 18 версии +nvm use + +# установите зависимости +npm i --legacy-peer-deps + +# билд документации по graphql +npm run build:graphql-docs + +# билд bruno query +npm run build:bruno-query + +# запустить next приложение ( терминал 1 ) +npm run dev:web + +# запустить приложение electron ( терминал 2 ) +npm run dev:electron +``` + +### Устранение неисправностей + +При запуске `npm install` может возникнуть ошибка `Unsupported platform`. Чтобы исправить это, необходимо удалить `node_modules` и `package-lock.json` и запустить `npm install`. В результате будут установлены все пакеты, необходимые для работы приложения. + +```shell +# Удаление node_modules в подкаталогах +find ./ -type d -name "node_modules" -print0 | while read -d $'\0' dir; do + rm -rf "$dir" +done + +# Удаление package-lock в подкаталогах +find . -type f -name "package-lock.json" -delete +``` + +### Тестирование + +```bash +# bruno-schema +npm test --workspace=packages/bruno-schema + +# bruno-lang +npm test --workspace=packages/bruno-lang +``` diff --git a/readme.md b/readme.md index 651d8cebe..29cb5ad93 100644 --- a/readme.md +++ b/readme.md @@ -6,10 +6,12 @@ [![GitHub version](https://badge.fury.io/gh/usebruno%2Fbruno.svg)](https://badge.fury.io/gh/usebruno%bruno) [![CI](https://github.com/usebruno/bruno/actions/workflows/unit-tests.yml/badge.svg?branch=main)](https://github.com/usebruno/bruno/workflows/unit-tests.yml) [![Commit Activity](https://img.shields.io/github/commit-activity/m/usebruno/bruno)](https://github.com/usebruno/bruno/pulse) -[![Twitter](https://img.shields.io/twitter/follow/use_bruno?style=social&logo=twitter)](https://twitter.com/use_bruno) +[![X](https://img.shields.io/twitter/follow/use_bruno?style=social&logo=x)](https://twitter.com/use_bruno) [![Website](https://img.shields.io/badge/Website-Visit-blue)](https://www.usebruno.com) [![Download](https://img.shields.io/badge/Download-Latest-brightgreen)](https://www.usebruno.com/downloads) +**English** | [Русский](/readme_ru.md) + Bruno is a new and innovative API client, aimed at revolutionizing the status quo represented by Postman and similar tools out there. Bruno stores your collections directly in a folder on your filesystem. We use a plain text markup language, Bru, to save information about API requests. diff --git a/readme_ru.md b/readme_ru.md new file mode 100644 index 000000000..8c25e5c57 --- /dev/null +++ b/readme_ru.md @@ -0,0 +1,79 @@ +
+ + +### Bruno - IDE с открытым исходным кодом для изучения и тестирования API. + +[![GitHub version](https://badge.fury.io/gh/usebruno%2Fbruno.svg)](https://badge.fury.io/gh/usebruno%bruno) +[![CI](https://github.com/usebruno/bruno/actions/workflows/unit-tests.yml/badge.svg?branch=main)](https://github.com/usebruno/bruno/workflows/unit-tests.yml) +[![Commit Activity](https://img.shields.io/github/commit-activity/m/usebruno/bruno)](https://github.com/usebruno/bruno/pulse) +[![X](https://img.shields.io/twitter/follow/use_bruno?style=social&logo=x)](https://twitter.com/use_bruno) +[![Website](https://img.shields.io/badge/Website-Visit-blue)](https://www.usebruno.com) +[![Download](https://img.shields.io/badge/Download-Latest-brightgreen)](https://www.usebruno.com/downloads) + +[English](/readme.md) | **Русский** + +Bruno - новый и инновационный клиент API, направленный на революцию в установившейся ситуации, представленной Postman и подобными инструментами. + +Bruno хранит ваши коллекции непосредственно в папке в вашей файловой системе. Для сохранения информации об API-запросах мы используем язык Bru. + +Для совместной работы над коллекциями API можно использовать git или любой другой контроль версий по вашему выбору. + +Bruno работает только в автономном режиме. Добавление облачной синхронизации в Bruno не планируется. Мы ценим конфиденциальность ваших данных и считаем, что они должны оставаться на вашем устройстве. Ознакомьтесь с нашим долгосрочным видением [здесь](https://github.com/usebruno/bruno/discussions/269) + +![bruno](assets/images/landing-2.png)

+ +### Работа на нескольких платформах 🖥️ + +![bruno](assets/images/run-anywhere.png)

+ +### Совместная работа через Git 👩‍💻🧑‍💻 + +Или другая система контроля версий по вашему выбору + +![bruno](assets/images/version-control.png)

+ +### Важные ссылки 📌 + +- [Наше долгосрочное видение](https://github.com/usebruno/bruno/discussions/269) +- [Roadmap](https://github.com/usebruno/bruno/discussions/384) +- [Документация](https://docs.usebruno.com) +- [Сайт](https://www.usebruno.com) +- [Скачать Bruno](https://www.usebruno.com/downloads) + +### Витрина 🎥 + +- [Отзывы](https://github.com/usebruno/bruno/discussions/343) +- [Центр знаний](https://github.com/usebruno/bruno/discussions/386) +- [Скриптомания](https://github.com/usebruno/bruno/discussions/385) + +### Поддержка ❤️ + +Гав! Если вам нравится проект, нажмите на звездочку ⭐ !!! + +### Поделись отзывами 📣 + +Если Бруно помог вам в работе и в ваших командах, пожалуйста, не забудьте поделиться своим [отзывом на нашем обсуждении в github](https://github.com/usebruno/bruno/discussions/343) + +### Внести вклад 👩‍💻🧑‍💻 + +Я рад, что Вы хотите улучшить Бруно. Пожалуйста, ознакомьтесь с [этим гайдом](contributing_ru.md) + +Даже если вы не можете внести свой вклад с помощью кода, пожалуйста, не стесняйтесь сообщать об ошибках и пожеланиях к функциям, которые необходимо реализовать для решения вашей задачи. + +### Авторы + + + +### Оставайтесь на связи 🌐 + +[X ( Twitter )](https://twitter.com/use_bruno)
+[Наш сайт](https://www.usebruno.com)
+[Discord](https://discord.com/invite/KgcZUncpjq) + +### Лицензия 📄 + +[MIT](license.md) From be8db1876a9eccc4e7f73d8604f1093ed109ff93 Mon Sep 17 00:00:00 2001 From: Anoop M D Date: Fri, 6 Oct 2023 04:02:51 +0530 Subject: [PATCH 21/23] feat(#111): refactor new request auto open logic --- packages/bruno-app/src/providers/App/index.js | 2 + .../providers/App/useCollectionNextAction.js | 35 ++++++++++++++++ .../providers/App/useCollectionTreeSync.js | 40 ++----------------- .../ReduxStore/slices/collections/actions.js | 35 +++++++++++++--- .../ReduxStore/slices/collections/index.js | 16 +++++++- 5 files changed, 85 insertions(+), 43 deletions(-) create mode 100644 packages/bruno-app/src/providers/App/useCollectionNextAction.js diff --git a/packages/bruno-app/src/providers/App/index.js b/packages/bruno-app/src/providers/App/index.js index 9768802fc..041bf6e9d 100644 --- a/packages/bruno-app/src/providers/App/index.js +++ b/packages/bruno-app/src/providers/App/index.js @@ -1,6 +1,7 @@ import React, { useEffect } from 'react'; import useTelemetry from './useTelemetry'; import useCollectionTreeSync from './useCollectionTreeSync'; +import useCollectionNextAction from './useCollectionNextAction'; import { useDispatch } from 'react-redux'; import { refreshScreenWidth } from 'providers/ReduxStore/slices/app'; import StyledWrapper from './StyledWrapper'; @@ -10,6 +11,7 @@ export const AppContext = React.createContext(); export const AppProvider = (props) => { useTelemetry(); useCollectionTreeSync(); + useCollectionNextAction(); const dispatch = useDispatch(); diff --git a/packages/bruno-app/src/providers/App/useCollectionNextAction.js b/packages/bruno-app/src/providers/App/useCollectionNextAction.js new file mode 100644 index 000000000..94c58f604 --- /dev/null +++ b/packages/bruno-app/src/providers/App/useCollectionNextAction.js @@ -0,0 +1,35 @@ +import React, { useEffect } from 'react'; +import get from 'lodash/get'; +import each from 'lodash/each'; +import { addTab } from 'providers/ReduxStore/slices/tabs'; +import { getDefaultRequestPaneTab, findItemInCollectionByPathname } from 'utils/collections/index'; +import { hideHomePage } from 'providers/ReduxStore/slices/app'; +import { updateNextAction } from 'providers/ReduxStore/slices/collections/index'; +import { useSelector, useDispatch } from 'react-redux'; + +const useCollectionNextAction = () => { + const collections = useSelector((state) => state.collections.collections); + const dispatch = useDispatch(); + + useEffect(() => { + each(collections, (collection) => { + if (collection.nextAction && collection.nextAction.type === 'OPEN_REQUEST') { + const item = findItemInCollectionByPathname(collection, get(collection, 'nextAction.payload.pathname')); + + if (item) { + dispatch(updateNextAction(collection.uid, null)); + dispatch( + addTab({ + uid: item.uid, + collectionUid: collection.uid, + requestPaneTab: getDefaultRequestPaneTab(item.type) + }) + ); + dispatch(hideHomePage()); + } + } + }); + }, [collections, each, dispatch, updateNextAction, hideHomePage, addTab]); +}; + +export default useCollectionNextAction; diff --git a/packages/bruno-app/src/providers/App/useCollectionTreeSync.js b/packages/bruno-app/src/providers/App/useCollectionTreeSync.js index ba7193f31..caf057d5b 100644 --- a/packages/bruno-app/src/providers/App/useCollectionTreeSync.js +++ b/packages/bruno-app/src/providers/App/useCollectionTreeSync.js @@ -1,5 +1,5 @@ import { useEffect } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; +import { useDispatch } from 'react-redux'; import { collectionAddDirectoryEvent, collectionAddFileEvent, @@ -17,15 +17,9 @@ import { import toast from 'react-hot-toast'; import { openCollectionEvent, collectionAddEnvFileEvent } from 'providers/ReduxStore/slices/collections/actions'; import { isElectron } from 'utils/common/platform'; -import { addTab } from 'providers/ReduxStore/slices/tabs'; -import { findCollectionByUid, getDefaultRequestPaneTab } from 'utils/collections/index'; -import { hideHomePage } from 'providers/ReduxStore/slices/app'; -import { updateLastAction } from 'providers/ReduxStore/slices/collections/index'; const useCollectionTreeSync = () => { const dispatch = useDispatch(); - const tabs = useSelector((state) => state.tabs.tabs); - const collections = useSelector((state) => state.collections.collections); useEffect(() => { if (!isElectron()) { @@ -56,25 +50,6 @@ const useCollectionTreeSync = () => { file: val }) ); - - const collectionUid = val.meta.collectionUid; - const lastAction = findCollectionByUid(collections, collectionUid)?.lastAction; - - // When the request was just created open it in a new tab - if (lastAction && lastAction.type === 'ADD_REQUEST') { - dispatch(updateLastAction({ lastAction: null, collectionUid })); - - if (lastAction.payload === val.data.name) { - dispatch( - addTab({ - uid: val.data.uid, - collectionUid: collectionUid, - requestPaneTab: getDefaultRequestPaneTab(val.data) - }) - ); - dispatch(hideHomePage()); - } - } } if (type === 'change') { dispatch( @@ -140,6 +115,8 @@ const useCollectionTreeSync = () => { dispatch(runRequestEvent(val)); }; + ipcRenderer.invoke('renderer:ready'); + const removeListener1 = ipcRenderer.on('main:collection-opened', _openCollection); const removeListener2 = ipcRenderer.on('main:collection-tree-updated', _collectionTreeUpdated); const removeListener3 = ipcRenderer.on('main:collection-already-opened', _collectionAlreadyOpened); @@ -167,16 +144,7 @@ const useCollectionTreeSync = () => { removeListener10(); removeListener11(); }; - }, [isElectron, tabs, collections]); - - useEffect(() => { - if (!isElectron()) { - return () => {}; - } - - const { ipcRenderer } = window; - ipcRenderer.invoke('renderer:ready'); - }, []); + }, [isElectron]); }; export default useCollectionTreeSync; diff --git a/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js b/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js index 7d5577c86..c1ec2ff3b 100644 --- a/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js +++ b/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js @@ -26,6 +26,7 @@ import { sendNetworkRequest, cancelNetworkRequest } from 'utils/network'; import { updateLastAction, + updateNextAction, resetRunResults, requestCancelled, responseReceived, @@ -39,8 +40,7 @@ import { renameCollection as _renameCollection, removeCollection as _removeCollection, sortCollections as _sortCollections, - collectionAddEnvFileEvent as _collectionAddEnvFileEvent, - updateNewRequest + collectionAddEnvFileEvent as _collectionAddEnvFileEvent } from './index'; import { closeAllCollectionTabs } from 'providers/ReduxStore/slices/tabs'; @@ -596,8 +596,19 @@ export const newHttpRequest = (params) => (dispatch, getState) => { const { ipcRenderer } = window; ipcRenderer.invoke('renderer:new-request', fullName, item).then(resolve).catch(reject); - // Add the new request name here so it can be opened in a new tab in useCollectionTreeSync.js - dispatch(updateLastAction({ lastAction: { type: 'ADD_REQUEST', payload: item.name }, collectionUid })); + // the useCollectionNextAction() will track this and open the new request in a new tab + // once the request is created + dispatch( + updateNextAction({ + nextAction: { + type: 'OPEN_REQUEST', + payload: { + pathname: fullName + } + }, + collectionUid + }) + ); } else { return reject(new Error('Duplicate request names are not allowed under the same folder')); } @@ -615,8 +626,20 @@ export const newHttpRequest = (params) => (dispatch, getState) => { const { ipcRenderer } = window; ipcRenderer.invoke('renderer:new-request', fullName, item).then(resolve).catch(reject); - // Add the new request name here so it can be opened in a new tab in useCollectionTreeSync.js - dispatch(updateLastAction({ lastAction: { type: 'ADD_REQUEST', payload: item.name }, collectionUid })); + + // the useCollectionNextAction() will track this and open the new request in a new tab + // once the request is created + dispatch( + updateNextAction({ + nextAction: { + type: 'OPEN_REQUEST', + payload: { + pathname: fullName + } + }, + collectionUid + }) + ); } else { return reject(new Error('Duplicate request names are not allowed under the same folder')); } diff --git a/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js b/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js index ec89bb85d..31cf6d6ca 100644 --- a/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js +++ b/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js @@ -39,6 +39,8 @@ export const collectionsSlice = createSlice({ createCollection: (state, action) => { const collectionUids = map(state.collections, (c) => c.uid); const collection = action.payload; + + // TODO: move this to use the nextAction approach // last action is used to track the last action performed on the collection // this is optional // this is used in scenarios where we want to know the last action performed on the collection @@ -47,6 +49,10 @@ export const collectionsSlice = createSlice({ collection.importedAt = new Date().getTime(); collection.lastAction = null; + // an improvement over the above approach. + // this defines an action that need to be performed next and is executed vy the useCollectionNextAction() + collection.nextAction = null; + collapseCollection(collection); addDepth(collection.items); if (!collectionUids.includes(collection.uid)) { @@ -93,6 +99,14 @@ export const collectionsSlice = createSlice({ collection.lastAction = lastAction; } }, + updateNextAction: (state, action) => { + const { collectionUid, nextAction } = action.payload; + const collection = findCollectionByUid(state.collections, collectionUid); + + if (collection) { + collection.nextAction = nextAction; + } + }, collectionUnlinkEnvFileEvent: (state, action) => { const { data: environment, meta } = action.payload; const collection = findCollectionByUid(state.collections, meta.collectionUid); @@ -1197,10 +1211,10 @@ export const { removeCollection, sortCollections, updateLastAction, + updateNextAction, collectionUnlinkEnvFileEvent, saveEnvironment, selectEnvironment, - updateNewRequest, newItem, deleteItem, renameItem, From a838185ddff440fab8a4f257de00ba7f2c8d69bc Mon Sep 17 00:00:00 2001 From: Anoop M D Date: Fri, 6 Oct 2023 04:18:44 +0530 Subject: [PATCH 22/23] chore: version bump v0.20.0 --- packages/bruno-app/src/components/Sidebar/index.js | 2 +- packages/bruno-electron/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/bruno-app/src/components/Sidebar/index.js b/packages/bruno-app/src/components/Sidebar/index.js index 9c8bbf4e0..ad7164371 100644 --- a/packages/bruno-app/src/components/Sidebar/index.js +++ b/packages/bruno-app/src/components/Sidebar/index.js @@ -105,7 +105,7 @@ const Sidebar = () => { Star -
v0.19.0
+
v0.20.0
diff --git a/packages/bruno-electron/package.json b/packages/bruno-electron/package.json index 6c81f5dc5..2761ba8d4 100644 --- a/packages/bruno-electron/package.json +++ b/packages/bruno-electron/package.json @@ -1,5 +1,5 @@ { - "version": "v0.19.0", + "version": "v0.20.0", "name": "bruno", "description": "Opensource API Client for Exploring and Testing APIs", "homepage": "https://www.usebruno.com", From c25542bbdffb2ca78a5e76a8d93934bd1a3ff9a2 Mon Sep 17 00:00:00 2001 From: Anoop M D Date: Fri, 6 Oct 2023 04:59:57 +0530 Subject: [PATCH 23/23] chore: bru cli v0.12.0 release --- packages/bruno-cli/changelog.md | 4 ++++ packages/bruno-cli/package.json | 2 +- packages/bruno-electron/package.json | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/bruno-cli/changelog.md b/packages/bruno-cli/changelog.md index 31b66ddc0..1b83060f4 100644 --- a/packages/bruno-cli/changelog.md +++ b/packages/bruno-cli/changelog.md @@ -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 diff --git a/packages/bruno-cli/package.json b/packages/bruno-cli/package.json index 326ad8fc4..4ba6edbd7 100644 --- a/packages/bruno-cli/package.json +++ b/packages/bruno-cli/package.json @@ -1,6 +1,6 @@ { "name": "@usebruno/cli", - "version": "0.11.0", + "version": "0.12.0", "license": "MIT", "main": "src/index.js", "bin": { diff --git a/packages/bruno-electron/package.json b/packages/bruno-electron/package.json index 2761ba8d4..11f30a66b 100644 --- a/packages/bruno-electron/package.json +++ b/packages/bruno-electron/package.json @@ -9,7 +9,7 @@ "scripts": { "clean": "rimraf dist", "dev": "electron .", - "dist": "electron-builder --win --config electron-builder-config.js", + "dist": "electron-builder --mac --config electron-builder-config.js", "pack": "electron-builder --dir", "test": "jest" },