From fbc4346d92ec7c2f2ba705f125468f0d631495cd Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Wed, 15 Apr 2026 19:53:08 +0200 Subject: [PATCH] feat(cli): display failure reasons in human-readable table output --- ui/cli/src/output/table.ts | 16 +++++-- ui/cli/tests/output.test.ts | 72 +++++++++++++++++++++++++++++++ ui/common/src/types/UIProtocol.ts | 2 + 3 files changed, 86 insertions(+), 4 deletions(-) diff --git a/ui/cli/src/output/table.ts b/ui/cli/src/output/table.ts index 4ce72808..b6b13e73 100644 --- a/ui/cli/src/output/table.ts +++ b/ui/cli/src/output/table.ts @@ -17,11 +17,19 @@ export const outputTable = (payload: ResponsePayload): void => { if (payload.hashIdsFailed != null && payload.hashIdsFailed.length > 0) { process.stderr.write(chalk.red(`✗ Failed (${payload.hashIdsFailed.length.toString()}):\n`)) - const table = new Table({ head: [chalk.white('Hash ID')] }) - for (const id of payload.hashIdsFailed) { - table.push([id]) + if (payload.responsesFailed != null && payload.responsesFailed.length > 0) { + const table = new Table({ head: [chalk.white('Hash ID'), chalk.white('Error')] }) + for (const entry of payload.responsesFailed) { + table.push([entry.hashId ?? '(unknown)', entry.errorMessage ?? 'Unknown error']) + } + process.stderr.write(table.toString() + '\n') + } else { + const table = new Table({ head: [chalk.white('Hash ID')] }) + for (const id of payload.hashIdsFailed) { + table.push([id]) + } + process.stderr.write(table.toString() + '\n') } - process.stderr.write(table.toString() + '\n') } if ( diff --git a/ui/cli/tests/output.test.ts b/ui/cli/tests/output.test.ts index e70fcf2e..fa2d7879 100644 --- a/ui/cli/tests/output.test.ts +++ b/ui/cli/tests/output.test.ts @@ -165,4 +165,76 @@ await describe('output formatters', async () => { }) assert.ok(output.includes('table err')) }) + + await it('should display responsesFailed with errorMessage in two-column table', () => { + const payload = { + hashIdsFailed: ['cs-001'], + responsesFailed: [ + { + command: 'startChargingStation', + errorMessage: 'Station not found', + hashId: 'cs-001', + status: ResponseStatus.FAILURE, + }, + ], + status: ResponseStatus.FAILURE, + } + const output = captureStderr(() => { + outputTable(payload) + }) + assert.ok(output.includes('Station not found')) + assert.ok(output.includes('cs-001')) + assert.ok(output.includes('Error')) + }) + + await it('should display responsesFailed with missing errorMessage as Unknown error', () => { + const payload = { + hashIdsFailed: ['cs-002'], + responsesFailed: [ + { + command: 'stopChargingStation', + hashId: 'cs-002', + status: ResponseStatus.FAILURE, + }, + ], + status: ResponseStatus.FAILURE, + } + const output = captureStderr(() => { + outputTable(payload) + }) + assert.ok(output.includes('Unknown error')) + assert.ok(output.includes('cs-002')) + }) + + await it('should display responsesFailed with undefined hashId as (unknown)', () => { + const payload = { + hashIdsFailed: ['cs-003'], + responsesFailed: [ + { + command: 'openConnection', + errorMessage: 'Timeout', + hashId: undefined, + status: ResponseStatus.FAILURE, + }, + ], + status: ResponseStatus.FAILURE, + } + const output = captureStderr(() => { + outputTable(payload) + }) + assert.ok(output.includes('(unknown)')) + assert.ok(output.includes('Timeout')) + }) + + await it('should display hashIdsFailed without responsesFailed as single-column table', () => { + const payload = { + hashIdsFailed: ['cs-001'], + status: ResponseStatus.FAILURE, + } + const output = captureStderr(() => { + outputTable(payload) + }) + assert.ok(output.includes('cs-001')) + assert.ok(!output.includes('Error')) + }) }) diff --git a/ui/common/src/types/UIProtocol.ts b/ui/common/src/types/UIProtocol.ts index 901573e7..6fb1e547 100644 --- a/ui/common/src/types/UIProtocol.ts +++ b/ui/common/src/types/UIProtocol.ts @@ -57,6 +57,8 @@ export enum ServerNotification { } export interface BroadcastChannelResponsePayload extends JsonObject { + command?: string + errorMessage?: string hashId: string | undefined status: ResponseStatus } -- 2.53.0