Skip to content

Commit d5863f4

Browse files
test results table
1 parent 4ecfc70 commit d5863f4

File tree

2 files changed

+237
-119
lines changed

2 files changed

+237
-119
lines changed

src/providers/maestro.ts

Lines changed: 81 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ export interface MaestroFlowInfo {
3333
status: MaestroFlowStatus;
3434
success?: number;
3535
test_case_id?: number;
36+
error_messages?: string[];
3637
}
3738

3839
export interface MaestroRunInfo {
@@ -48,6 +49,7 @@ export interface MaestroRunInfo {
4849
options?: Record<string, unknown>;
4950
assets?: MaestroRunAssets;
5051
flows?: MaestroFlowInfo[];
52+
error_messages?: string[];
5153
}
5254

5355
export interface MaestroRunDetails extends MaestroRunInfo {
@@ -869,7 +871,6 @@ export default class Maestro {
869871
// Check for version update notification
870872
const latestVersion = response.headers?.['x-testingbotctl-version'];
871873
utils.checkForUpdate(latestVersion);
872-
873874
return response.data;
874875
} catch (error) {
875876
throw new TestingBotError(`Failed to get Maestro test status`, {
@@ -924,11 +925,14 @@ export default class Maestro {
924925
}
925926

926927
if (allFlows.length > 0) {
928+
// Check if any flow has failed (for showing error column)
929+
const hasFailures = this.hasAnyFlowFailed(allFlows);
930+
927931
if (!flowsTableDisplayed) {
928932
// First time showing flows - display header and initial state
929933
console.log(); // Empty line before flows table
930-
this.displayFlowsTableHeader();
931-
displayedLineCount = this.displayFlowsWithLimit(allFlows, previousFlowStatus);
934+
this.displayFlowsTableHeader(hasFailures);
935+
displayedLineCount = this.displayFlowsWithLimit(allFlows, previousFlowStatus, hasFailures);
932936
flowsTableDisplayed = true;
933937
} else {
934938
// Update flows in place
@@ -941,6 +945,26 @@ export default class Maestro {
941945
}
942946

943947
if (status.completed) {
948+
// Display final flows table with error messages if there are failures
949+
if (!this.options.quiet && flowsTableDisplayed) {
950+
const allFlows: MaestroFlowInfo[] = [];
951+
for (const run of status.runs) {
952+
if (run.flows && run.flows.length > 0) {
953+
allFlows.push(...run.flows);
954+
}
955+
}
956+
957+
const hasFailures = this.hasAnyFlowFailed(allFlows);
958+
if (hasFailures) {
959+
// Clear previous in-place display and redraw with error messages
960+
console.log(); // Empty line before final table
961+
this.displayFlowsTableHeader(true);
962+
for (const flow of allFlows) {
963+
this.displayFlowRow(flow, false, true);
964+
}
965+
}
966+
}
967+
944968
// Print final summary
945969
if (!this.options.quiet) {
946970
console.log(); // Empty line before summary
@@ -963,12 +987,7 @@ export default class Maestro {
963987
}
964988
} else {
965989
const failedRuns = status.runs.filter((run) => run.success !== 1);
966-
logger.error(`${failedRuns.length} test run(s) failed:`);
967-
for (const run of failedRuns) {
968-
logger.error(
969-
` - Run ${run.id} (${run.capabilities.deviceName}): ${run.report}`,
970-
);
971-
}
990+
logger.error(`${failedRuns.length} test run(s) failed`);
972991
}
973992

974993
if (this.options.report && this.options.reportOutputDir) {
@@ -1082,6 +1101,15 @@ export default class Maestro {
10821101
}
10831102
}
10841103

1104+
private hasAnyFlowFailed(flows: MaestroFlowInfo[]): boolean {
1105+
return flows.some(
1106+
(flow) =>
1107+
(flow.status === 'DONE' && flow.success !== 1) ||
1108+
flow.status === 'FAILED' ||
1109+
(flow.error_messages && flow.error_messages.length > 0),
1110+
);
1111+
}
1112+
10851113
private calculateFlowDuration(flow: MaestroFlowInfo): string {
10861114
if (!flow.requested_at) {
10871115
return '-';
@@ -1160,15 +1188,15 @@ export default class Maestro {
11601188
private displayFlowsWithLimit(
11611189
flows: MaestroFlowInfo[],
11621190
previousFlowStatus: Map<number, MaestroFlowStatus>,
1191+
hasFailures: boolean = false,
11631192
): number {
11641193
const maxFlows = this.getMaxDisplayableFlows();
11651194
const displayFlows = flows.slice(0, maxFlows);
11661195
let linesWritten = 0;
11671196

11681197
for (const flow of displayFlows) {
1169-
this.displayFlowRow(flow, false);
1198+
linesWritten += this.displayFlowRow(flow, false, hasFailures);
11701199
previousFlowStatus.set(flow.id, flow.status);
1171-
linesWritten++;
11721200
}
11731201

11741202
// Show summary for remaining flows
@@ -1181,37 +1209,70 @@ export default class Maestro {
11811209
return linesWritten;
11821210
}
11831211

1184-
private displayFlowsTableHeader(): void {
1185-
const header = ` ${'Duration'.padEnd(10)} ${'Status'.padEnd(8)} Test`;
1186-
const separator = ` ${'─'.repeat(10)} ${'─'.repeat(8)} ${'─'.repeat(40)}`;
1212+
private displayFlowsTableHeader(hasFailures: boolean = false): void {
1213+
let header = ` ${'Duration'.padEnd(10)} ${'Status'.padEnd(8)} Test`;
1214+
let separator = ` ${'─'.repeat(10)} ${'─'.repeat(8)} ${'─'.repeat(30)}`;
1215+
1216+
if (hasFailures) {
1217+
header += ' Fail reason';
1218+
separator += ` ${'─'.repeat(80)}`;
1219+
}
1220+
11871221
console.log(colors.dim(header));
11881222
console.log(colors.dim(separator));
11891223
}
11901224

1191-
private displayFlowRow(flow: MaestroFlowInfo, isUpdate: boolean = false): void {
1225+
private displayFlowRow(
1226+
flow: MaestroFlowInfo,
1227+
isUpdate: boolean = false,
1228+
hasFailures: boolean = false,
1229+
): number {
11921230
const duration = this.calculateFlowDuration(flow).padEnd(10);
11931231
const statusDisplay = this.getFlowStatusDisplay(flow);
11941232
// Pad based on display text length, add extra for color codes
11951233
const statusPadded = statusDisplay.colored + ' '.repeat(Math.max(0, 8 - statusDisplay.text.length));
1196-
const name = flow.name;
1234+
const name = flow.name.padEnd(30);
11971235

1198-
const row = ` ${duration} ${statusPadded} ${name}`;
1236+
let linesWritten = 0;
1237+
const isFailed = flow.status === 'DONE' && flow.success !== 1;
1238+
const errorMessages = flow.error_messages || [];
1239+
1240+
// Build the main row
1241+
let row = ` ${duration} ${statusPadded} ${name}`;
1242+
1243+
// Add first error message on the same line if failed and has errors
1244+
if (hasFailures && isFailed && errorMessages.length > 0) {
1245+
row += ` ${colors.red(errorMessages[0])}`;
1246+
}
11991247

12001248
if (isUpdate) {
1201-
// Move cursor up and clear line before writing
12021249
process.stdout.write(`\r${row}`);
12031250
} else {
12041251
console.log(row);
12051252
}
1253+
linesWritten++;
1254+
1255+
// Display remaining error messages on continuation lines
1256+
if (!isUpdate && hasFailures && isFailed && errorMessages.length > 1) {
1257+
// Indent to align with the Fail reason column: Duration(11) + Status(9) + Test(31) = 51 chars
1258+
const indent = ' '.repeat(51);
1259+
for (let i = 1; i < errorMessages.length; i++) {
1260+
console.log(`${indent} ${colors.red(errorMessages[i])}`);
1261+
linesWritten++;
1262+
}
1263+
}
1264+
1265+
return linesWritten;
12061266
}
12071267

12081268
private displayFlowsTable(
12091269
flows: MaestroFlowInfo[],
12101270
previousFlowStatus: Map<number, MaestroFlowStatus>,
12111271
showHeader: boolean,
1272+
hasFailures: boolean = false,
12121273
): number {
12131274
if (showHeader) {
1214-
this.displayFlowsTableHeader();
1275+
this.displayFlowsTableHeader(hasFailures);
12151276
}
12161277

12171278
let linesWritten = 0;
@@ -1221,8 +1282,7 @@ export default class Maestro {
12211282
const isNewFlow = prevStatus === undefined;
12221283

12231284
if (isNewFlow) {
1224-
this.displayFlowRow(flow, false);
1225-
linesWritten++;
1285+
linesWritten += this.displayFlowRow(flow, false, hasFailures);
12261286
}
12271287

12281288
previousFlowStatus.set(flow.id, flow.status);

0 commit comments

Comments
 (0)