import {
ExecutionContext,
ExecutionResult,
TestStatistics,
TestResult,
CollectionRunParams,
} from './models.js';
import newman from 'newman';
export class OutputBuilder {
private readonly lines: string[] = [];
add(line: string): void {
this.lines.push(line);
}
build(): string {
return this.lines.join('\n');
}
}
export class TestTracker {
private readonly assertions: TestResult[] = [];
private totalTests = 0;
private totalPassed = 0;
private totalFailed = 0;
addAssertion(assertion: TestResult): void {
this.assertions.push(assertion);
this.totalTests++;
if (assertion.passed) {
this.totalPassed++;
} else {
this.totalFailed++;
}
}
displayCurrentResults(): string {
if (this.assertions.length === 0) {
return '';
}
const lines: string[] = [' π Test Results:'];
this.assertions.forEach((assertion) => {
const status = assertion.passed ? 'β' : 'β';
const name = assertion.assertion || assertion.name || 'Unnamed test';
lines.push(` ${status} ${name}`);
if (!assertion.passed && assertion.error) {
const errorMessage =
typeof assertion.error === 'string'
? assertion.error
: assertion.error.message || 'Unknown error';
lines.push(` ββ Error: ${errorMessage}`);
}
});
const passed = this.assertions.filter((a) => a.passed).length;
const failed = this.assertions.filter((a) => !a.passed).length;
lines.push(` ββββββββββββββββββββββββββββββββββββββββ`);
lines.push(` ${this.assertions.length} tests | β ${passed} passed | β ${failed} failed\n`);
this.assertions.length = 0;
return lines.join('\n');
}
getTotalStats(): TestStatistics {
return {
total: this.totalTests,
passed: this.totalPassed,
failed: this.totalFailed,
};
}
reset(): void {
this.assertions.length = 0;
this.totalTests = 0;
this.totalPassed = 0;
this.totalFailed = 0;
}
}
export function buildNewmanOptions(
params: CollectionRunParams,
collection: object,
environment?: object
) {
return {
collection: collection,
environment: environment,
iterationCount: params.iterationCount || 1,
timeout: params.requestTimeout || 60000,
timeoutRequest: params.requestTimeout || 60000,
timeoutScript: params.scriptTimeout || 5000,
delayRequest: 1000,
ignoreRedirects: false,
insecure: false,
bail: params.stopOnFailure ? ['failure'] : false,
suppressExitCode: true,
reporters: [],
reporter: {},
color: 'off',
verbose: false,
requestAgents: {
http: undefined,
https: undefined,
},
};
}
export async function executeCollection(context: ExecutionContext): Promise<ExecutionResult> {
const tracker = new TestTracker();
const output = new OutputBuilder();
output.add(`π Starting collection: ${context.collection.name}`);
if (context.environment) {
output.add(`π Using environment: ${context.environment.name}\n`);
}
const newmanOptions = buildNewmanOptions(
context.params,
context.collection.json,
context.environment?.json
);
const startTime = Date.now();
const summary = await runNewman(newmanOptions, tracker, output);
const endTime = Date.now();
const durationMs = endTime - startTime;
return {
output: output.build(),
testStats: tracker.getTotalStats(),
summary,
startTime,
endTime,
durationMs,
};
}
function runNewman(options: any, tracker: TestTracker, output: OutputBuilder): Promise<any> {
return new Promise((resolve, reject) => {
newman
.run(options)
.on('start', () => {
output.add('π― Starting collection run...\n');
})
.on('assertion', (_err: any, args: any) => {
if (args.assertion) {
tracker.addAssertion({
passed: !args.error,
assertion: args.assertion,
name: args.assertion,
error: args.error,
});
}
})
.on('item', (_err: any, args: any) => {
if (args.item) {
const testResults = tracker.displayCurrentResults();
if (testResults) {
output.add(`\nπ Request: ${args.item.name}`);
output.add(testResults);
}
}
})
.on('done', (err: any, summary: any) => {
if (err) {
output.add('\nβ Run error: ' + err.message);
reject(err);
return;
}
output.add('\n=== β
Run completed! ===');
appendSummaryToOutput(output, tracker, summary);
resolve(summary);
});
});
}
function appendSummaryToOutput(output: OutputBuilder, tracker: TestTracker, summary: any): void {
const testStats = tracker.getTotalStats();
if (testStats.total > 0) {
output.add('\nπ Overall Test Statistics:');
output.add(` Total tests: ${testStats.total}`);
output.add(` Passed: ${testStats.passed} β
`);
output.add(` Failed: ${testStats.failed} β`);
output.add(` Success rate: ${((testStats.passed / testStats.total) * 100).toFixed(1)}%`);
}
if (summary?.run?.stats) {
output.add('\nπ Request Summary:');
output.add(` Total requests: ${summary.run.stats.requests?.total || 0}`);
output.add(` Failed requests: ${summary.run.stats.requests?.failed || 0}`);
output.add(` Total assertions: ${summary.run.stats.assertions?.total || 0}`);
output.add(` Failed assertions: ${summary.run.stats.assertions?.failed || 0}`);
if (summary.run.stats.iterations) {
output.add(` Total iterations: ${summary.run.stats.iterations.total || 0}`);
output.add(` Failed iterations: ${summary.run.stats.iterations.failed || 0}`);
}
}
}