From 4321846dbde3064e4840774987a459c588644dda Mon Sep 17 00:00:00 2001 From: Sanjai Kumar <161328623+sanjaikumar-bruno@users.noreply.github.com> Date: Thu, 18 Sep 2025 15:20:28 +0530 Subject: [PATCH] feat: Add end-to-end tests for collection run reports (#5562) * feat: Add end-to-end tests for collection run reports --- .../collection-run-report.spec.ts | 43 +++++++++++++++++++ .../cli-junit-report-default-linux.xml | 29 +++++++++++++ .../collection/api/v1/posts.bru | 38 ++++++++++++++++ .../collection/api/v1/users.bru | 34 +++++++++++++++ .../collection/auth/login.bru | 38 ++++++++++++++++ .../collection/auth/logout.bru | 25 +++++++++++ .../collection/bruno.json | 9 ++++ 7 files changed, 216 insertions(+) create mode 100644 tests/runner/collection-run-report/collection-run-report.spec.ts create mode 100644 tests/runner/collection-run-report/collection-run-report.spec.ts-snapshots/cli-junit-report-default-linux.xml create mode 100644 tests/runner/collection-run-report/collection/api/v1/posts.bru create mode 100644 tests/runner/collection-run-report/collection/api/v1/users.bru create mode 100644 tests/runner/collection-run-report/collection/auth/login.bru create mode 100644 tests/runner/collection-run-report/collection/auth/logout.bru create mode 100644 tests/runner/collection-run-report/collection/bruno.json diff --git a/tests/runner/collection-run-report/collection-run-report.spec.ts b/tests/runner/collection-run-report/collection-run-report.spec.ts new file mode 100644 index 000000000..f7c3aabef --- /dev/null +++ b/tests/runner/collection-run-report/collection-run-report.spec.ts @@ -0,0 +1,43 @@ +import { test, expect } from '../../../playwright'; +import { execSync } from 'child_process'; +import fs from 'fs'; +import path from 'path'; + +function normalizeJunitReport(xmlContent: string): string { + return xmlContent + // Replace timestamps with fixed value + .replace(/timestamp="[^"]*"/g, 'timestamp="2024-01-01T00:00:00.000"') + // Replace hostnames with fixed value + .replace(/hostname="[^"]*"/g, 'hostname="test-host"') + // Replace execution times with fixed value + .replace(/time="[^"]*"/g, 'time="0.100"') + // Replace file paths with normalized path + .replace(/name="[^"]*\/[^"]*"/g, 'name="/test/path/collection"'); +} + +test.describe('Collection Run Report Tests', () => { + const collectionPath = path.join(__dirname, 'collection'); + + test('CLI: Run collection and generate JUnit report', async ({ createTmpDir }) => { + const outputDir = await createTmpDir('junit-report'); + const junitOutputPath = path.join(outputDir, 'cli-report.xml'); + + // Run collection via CLI with JUnit reporter + const command = `cd "${collectionPath}" && node ../../../../packages/bruno-cli/bin/bru.js run --reporter-junit "${junitOutputPath}"`; + + try { + execSync(command, { stdio: 'pipe' }); + } catch (error) { + // CLI may exit with non-zero code if tests fail, which is expected + console.log('CLI execution completed with exit code:', error.status); + } + + // Verify report was generated + expect(fs.existsSync(junitOutputPath)).toBe(true); + const junitReportContent = fs.readFileSync(junitOutputPath, 'utf8'); + + // Snapshot the normalized XML + const normalizedJunitReport = normalizeJunitReport(junitReportContent); + expect(normalizedJunitReport).toMatchSnapshot('cli-junit-report.xml'); + }); +}); diff --git a/tests/runner/collection-run-report/collection-run-report.spec.ts-snapshots/cli-junit-report-default-linux.xml b/tests/runner/collection-run-report/collection-run-report.spec.ts-snapshots/cli-junit-report-default-linux.xml new file mode 100644 index 000000000..daf9d79d4 --- /dev/null +++ b/tests/runner/collection-run-report/collection-run-report.spec.ts-snapshots/cli-junit-report-default-linux.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/runner/collection-run-report/collection/api/v1/posts.bru b/tests/runner/collection-run-report/collection/api/v1/posts.bru new file mode 100644 index 000000000..93891b857 --- /dev/null +++ b/tests/runner/collection-run-report/collection/api/v1/posts.bru @@ -0,0 +1,38 @@ +meta { + name: Get UUID + type: http + seq: 2 +} + +get { + url: https://httpbin.org/uuid + body: none + auth: none +} + +headers { + Accept: application/json +} + +tests { + test("This test will fail", function() { + expect(res.getStatus()).to.equal(404); // Intentional failure + }); + + test("Status code is 200", function() { + expect(res.getStatus()).to.equal(200); + }); + + test("Response is an object", function() { + expect(res.getBody()).to.be.an('object'); + }); + + test("Response has uuid property", function() { + expect(res.getBody()).to.have.property('uuid'); + }); + + test("UUID is a string", function() { + const body = res.getBody(); + expect(body.uuid).to.be.a('string'); + }); +} diff --git a/tests/runner/collection-run-report/collection/api/v1/users.bru b/tests/runner/collection-run-report/collection/api/v1/users.bru new file mode 100644 index 000000000..f1807c7c0 --- /dev/null +++ b/tests/runner/collection-run-report/collection/api/v1/users.bru @@ -0,0 +1,34 @@ +meta { + name: Get User Info + type: http + seq: 1 +} + +get { + url: https://httpbin.org/json + body: none + auth: none +} + +headers { + Accept: application/json +} + +tests { + test("Status code is 200", function() { + expect(res.getStatus()).to.equal(200); + }); + + test("Response is an object", function() { + expect(res.getBody()).to.be.an('object'); + }); + + test("Response has slideshow property", function() { + expect(res.getBody()).to.have.property('slideshow'); + }); + + test("Slideshow has title", function() { + const body = res.getBody(); + expect(body.slideshow).to.have.property('title'); + }); +} diff --git a/tests/runner/collection-run-report/collection/auth/login.bru b/tests/runner/collection-run-report/collection/auth/login.bru new file mode 100644 index 000000000..34bc8ceb1 --- /dev/null +++ b/tests/runner/collection-run-report/collection/auth/login.bru @@ -0,0 +1,38 @@ +meta { + name: Login Request + type: http + seq: 3 +} + +post { + url: https://httpbin.org/post + body: json + auth: none +} + +headers { + Content-Type: application/json +} + +body:json { + { + "username": "testuser", + "password": "testpass" + } +} + +tests { + test("Status code is 200", function() { + expect(res.getStatus()).to.equal(200); + }); + + test("Response has json field", function() { + const response = res.getBody(); + expect(response).to.have.property('json'); + }); + + test("Response json has username", function() { + const response = res.getBody(); + expect(response.json).to.have.property('username'); + }); +} diff --git a/tests/runner/collection-run-report/collection/auth/logout.bru b/tests/runner/collection-run-report/collection/auth/logout.bru new file mode 100644 index 000000000..5a7898b2f --- /dev/null +++ b/tests/runner/collection-run-report/collection/auth/logout.bru @@ -0,0 +1,25 @@ +meta { + name: Logout Request + type: http + seq: 4 +} + +delete { + url: https://httpbin.org/delete + body: none + auth: none +} + +headers { + Accept: application/json +} + +tests { + test("This test will also fail", function() { + expect(res.getStatus()).to.equal(500); // Intentional failure + }); + + test("Status code is 200", function() { + expect(res.getStatus()).to.equal(200); + }); +} diff --git a/tests/runner/collection-run-report/collection/bruno.json b/tests/runner/collection-run-report/collection/bruno.json new file mode 100644 index 000000000..337e873e4 --- /dev/null +++ b/tests/runner/collection-run-report/collection/bruno.json @@ -0,0 +1,9 @@ +{ + "version": "1", + "name": "Report Test Collection", + "type": "collection", + "ignore": [ + "node_modules", + ".git" + ] +}