-
Notifications
You must be signed in to change notification settings - Fork 0
/
flowerRunner.ts
139 lines (121 loc) · 5.24 KB
/
flowerRunner.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
import { parseYaml } from './utilities/yamlParser';
import { parseJson } from './utilities/jsonParser';
import { performAssertion } from './utilities/assertions';
import httpRequest from './clients/httpRequest';
import * as fs from 'fs';
import * as path from 'path';
import pc from 'picocolors';
interface Context {
[key: string]: any;
}
async function parseFile(filePath: string) {
const extension = filePath.split('.').pop()?.toLowerCase();
if (extension === 'yaml' || extension === 'yml') {
return await parseYaml(filePath);
} else if (extension === 'json') {
return await parseJson(filePath);
} else {
console.error(pc.red(`File extension ${extension} not supported`));
return null;
}
}
function getValueByPath(obj: any, pathString: string): any {
return pathString.split('.').reduce((acc, part) => acc && acc[part] !== undefined ? acc[part] : null, obj);
}
function elapsedTime(start: [number, number]): string {
const [secs, nanosecs] = process.hrtime(start);
const hours = Math.floor(secs / 3600);
const minutes = Math.floor((secs % 3600) / 60);
const seconds = secs % 60;
const milliseconds = Math.round(nanosecs / 1e6);
let timeString = '';
if (hours > 0) timeString += `${hours}h `;
if (hours > 0 || minutes > 0) timeString += `${minutes}m `;
timeString += `${seconds}s ${milliseconds}ms`;
return timeString.trim();
}
async function runTestFlowForFile(filePath: string): Promise<string> {
const flowStartTime = process.hrtime();
const flow = await parseFile(filePath);
if (!flow) {
return pc.red(`Failed to parse file: ${filePath}`);
}
const outputBuffer: string[] = [];
const log = (message: string) => outputBuffer.push(message);
for (const testName in flow) {
const context: Context = {};
log(`Running test: ${pc.cyan(testName)}\n`);
const steps = flow[testName];
for (const step of steps) {
const stepStartTime = process.hrtime();
const { name, url, method, params, assertions, headers, saveToContext } = step;
try {
log(pc.bold(` Running step: ${pc.cyan(name)}`));
const response = await httpRequest({ url, method, params, headers, context });
if (saveToContext) {
Object.keys(saveToContext).forEach((key: string) => {
const valuePath: string = saveToContext[key];
context[key] = getValueByPath(response, valuePath);
});
}
if (assertions) {
assertions.forEach((assertion: any, index: number) => {
const actualValue = getValueByPath(response, assertion.target);
try {
performAssertion(assertion, actualValue);
log(` Assertion ${index + 1}: ${pc.green('Passed')}`);
} catch (error) {
const message = (error as Error).message;
log(` Assertion ${index + 1}: ${pc.red('Failed')} - ${message || error}`);
}
});
}
} catch (error) {
const message = (error as Error).message;
log(pc.bgRed(` Step "${name}" encountered an error: ${message || error}`));
} finally {
log(pc.bold(` Completed in ${elapsedTime(stepStartTime)}\n`));
}
}
}
log(pc.cyan(`Test flow completed in ${elapsedTime(flowStartTime)}\n`));
return outputBuffer.join('\n');
}
async function runAllTestFlows(runInParallel: boolean) {
const overallStartTime = process.hrtime();
const testsDir = './tests';
const files = fs.readdirSync(testsDir).filter(file => file.endsWith('.yaml') || file.endsWith('.yml') || file.endsWith('.json'));
console.log(`\nRunning ${files.length} test flows...`);
console.log('Parallel mode: ', runInParallel);
let hasFailures = false;
if (runInParallel) {
const promises = files.map(file => runTestFlowForFile(path.join(testsDir, file)));
const results = await Promise.all(promises);
results.forEach((result, index) => {
console.log(pc.bgYellow(`\n${files[index]}`));
console.log(result);
if (result.includes('Failed')) {
hasFailures = true;
}
});
} else {
for (const file of files) {
console.log(pc.bgYellow(`\nProcessing file: ${file}`));
const result = await runTestFlowForFile(path.join(testsDir, file));
console.log(result);
if (result && result.includes('Failed')) {
hasFailures = true;
}
}
}
const totalElapsedTime = elapsedTime(overallStartTime);
console.log(`Total execution time: ${totalElapsedTime}`);
if (hasFailures) {
console.error(pc.bgRed('\nSome tests failed. Exiting with error code 1.'));
process.exit(1);
} else {
console.log(pc.bgGreen('\nTest execution completed successfully.'));
}
}
const runInParallel = process.argv.includes('--parallel');
runAllTestFlows(runInParallel).catch(console.error);