From c61833261b957e0ca678375a84fe8aa77eae2131 Mon Sep 17 00:00:00 2001 From: gonzaloriestra <14979109+gonzaloriestra@users.noreply.github.com> Date: Tue, 30 Jun 2026 00:33:21 +0000 Subject: [PATCH] [Refactor] Modernize errorHandler in cli-kit Modernize the errorHandler function by replacing Promise chains with async/await and using early returns to reduce nesting. Also updated associated tests to improve mocking and coverage. --- .../src/public/node/error-handler.test.ts | 42 ++++++++++++++++--- .../cli-kit/src/public/node/error-handler.ts | 18 ++++---- 2 files changed, 44 insertions(+), 16 deletions(-) diff --git a/packages/cli-kit/src/public/node/error-handler.test.ts b/packages/cli-kit/src/public/node/error-handler.test.ts index 8f101ab55d4..40a4b3cab00 100644 --- a/packages/cli-kit/src/public/node/error-handler.test.ts +++ b/packages/cli-kit/src/public/node/error-handler.test.ts @@ -1,5 +1,6 @@ import {errorHandler, cleanStackFrameFilePath, addBugsnagMetadata, sendErrorToBugsnag} from './error-handler.js' import * as metadata from './metadata.js' +import {reportAnalyticsEvent} from './analytics.js' import {ciPlatform, cloudEnvironment, isUnitTest, macAddress} from './context/local.js' import {mockAndCaptureOutput} from './testing/output.js' import * as error from './error.js' @@ -38,16 +39,22 @@ vi.mock('@bugsnag/js', () => { } }) vi.mock('./cli.js') +vi.mock('./analytics.js') +vi.mock('./ui.js') vi.mock('./context/local.js') vi.mock('../../public/node/crypto.js') vi.mock('../../private/node/context/service.js') vi.mock('../../private/node/session.js') -vi.mock('@oclif/core', () => ({ - settings: { - debug: false, - }, - Interfaces: {}, -})) +vi.mock('@oclif/core', async (importOriginal) => { + const actual: any = await importOriginal() + return { + ...actual, + settings: { + debug: false, + }, + Interfaces: {}, + } +}) beforeEach(() => { vi.mocked(ciPlatform).mockReturnValue({isCI: true, name: 'vitest', metadata: {}}) @@ -64,6 +71,29 @@ beforeEach(() => { }) describe('errorHandler', async () => { + test('handles generic errors', async () => { + // Given + const mockOutput = mockAndCaptureOutput() + const err = new Error('Generic error') + const config = { + runHook: vi.fn(), + plugins: [], + } as any + + // When + await errorHandler(err, config) + + // Then + expect(reportAnalyticsEvent).toHaveBeenCalledWith( + expect.objectContaining({ + errorMessage: 'Generic error', + exitMode: 'unexpected_error', + }), + ) + expect(onNotify).toHaveBeenCalled() + expect(mockOutput.info()).toMatch('') + }) + test('finishes the execution without exiting the proccess when cancel execution exception is raised', async () => { // Given vi.spyOn(process, 'exit').mockResolvedValue(null as never) diff --git a/packages/cli-kit/src/public/node/error-handler.ts b/packages/cli-kit/src/public/node/error-handler.ts index d639c9d3d33..43b788afcf6 100644 --- a/packages/cli-kit/src/public/node/error-handler.ts +++ b/packages/cli-kit/src/public/node/error-handler.ts @@ -36,17 +36,15 @@ export async function errorHandler( if (error.message && error.message !== '') { outputInfo(`✨ ${error.message}`) } - } else if (error instanceof AbortSilentError) { - /* empty */ - } else { - return errorMapper(error) - .then((error) => { - return handler(error) - }) - .then((mappedError) => { - return reportError(mappedError, config) - }) + return } + if (error instanceof AbortSilentError) { + return + } + + const intermediateError = await errorMapper(error) + const mappedError = await handler(intermediateError) + return reportError(mappedError, config) } const reportError = async (error: unknown, config?: Interfaces.Config): Promise => {