From 1c80ac88c1b8ae2ac46180c8e8f97ff0d505a059 Mon Sep 17 00:00:00 2001 From: zaldih Date: Sat, 10 Jan 2026 13:35:54 +0100 Subject: [PATCH 1/4] test(cli): add verify npkill starts in non-TTY environments --- tests/cli/cli.controller.test.ts | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/tests/cli/cli.controller.test.ts b/tests/cli/cli.controller.test.ts index 3e063df8..cc0bf4c5 100644 --- a/tests/cli/cli.controller.test.ts +++ b/tests/cli/cli.controller.test.ts @@ -196,8 +196,14 @@ describe('CliController test', () => { configServiceMock as unknown as ConfigService, ); - Object.defineProperty(process.stdout, 'columns', { value: 80 }); - Object.defineProperty(process.stdout, 'isTTY', { value: true }); + Object.defineProperty(process.stdout, 'columns', { + value: 80, + configurable: true, + }); + Object.defineProperty(process.stdout, 'isTTY', { + value: true, + configurable: true, + }); showHelpSpy = jest .spyOn(cliController, 'showHelp') @@ -398,5 +404,20 @@ describe('CliController test', () => { expect(exitWithErrorSpy).toHaveBeenCalledTimes(1); }); }); + + describe('TTY Handling', () => { + it('Should run normally even if stdout is NOT TTY', () => { + Object.defineProperty(process.stdout, 'isTTY', { value: false }); + cliController.init(); + expect(scanSpy).toHaveBeenCalledTimes(1); + }); + + it('Should exit if terminal is too small', () => { + Object.defineProperty(process.stdout, 'columns', { value: 10 }); + const exitWithErrorSpy = spyMethod('exitWithError'); + cliController.init(); + expect(exitWithErrorSpy).toHaveBeenCalledTimes(1); + }); + }); }); }); From 3ae85da712a81e39e7b0cfc81fe974b42757e04f Mon Sep 17 00:00:00 2001 From: zaldih Date: Sat, 10 Jan 2026 13:38:06 +0100 Subject: [PATCH 2/4] fix(cli): allow execution in non-TTY environments --- src/cli/cli.controller.ts | 5 ----- src/cli/services/ui.service.ts | 4 +++- src/constants/messages.constants.ts | 4 ---- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/cli/cli.controller.ts b/src/cli/cli.controller.ts index 1472c625..4897c719 100644 --- a/src/cli/cli.controller.ts +++ b/src/cli/cli.controller.ts @@ -607,11 +607,6 @@ export class CliController { this.logger.error(INFO_MSGS.MIN_CLI_CLOMUNS); this.exitWithError(); } - if (!this.stdout.isTTY) { - this.uiService.print(INFO_MSGS.NO_TTY); - this.logger.error(INFO_MSGS.NO_TTY); - this.exitWithError(); - } } private checkFileRequirements(): void { diff --git a/src/cli/services/ui.service.ts b/src/cli/services/ui.service.ts index 4900a36e..f3c59d4c 100644 --- a/src/cli/services/ui.service.ts +++ b/src/cli/services/ui.service.ts @@ -7,7 +7,9 @@ export class UiService { uiComponents: BaseUi[] = []; setRawMode(set = true): void { - this.stdin.setRawMode(set); + if (this.stdin.isTTY) { + this.stdin.setRawMode(set); + } process.stdin.resume(); } diff --git a/src/constants/messages.constants.ts b/src/constants/messages.constants.ts index 4dd00417..f746e356 100644 --- a/src/constants/messages.constants.ts +++ b/src/constants/messages.constants.ts @@ -15,10 +15,6 @@ export const INFO_MSGS = { 'Oh no! The terminal is too narrow. Please, ' + 'enlarge it (This will be fixed in future versions. Disclose the inconveniences)', NEW_UPDATE_FOUND: 'New version found! npm i -g npkill for update.', - NO_TTY: - 'Oh no! Npkill does not support this terminal (TTY is required). This ' + - 'is a bug, which has to be fixed. Please try another command interpreter ' + - '(for example, CMD in windows)', NO_VALID_SORT_NAME: 'Invalid sort option. Available: path | size | last-mod', NO_VALID_SIZE_UNIT: 'Invalid size-unit option. Available: auto | mb | gb', STARTING: 'Initializing ', From f831368638c460a1e2789da11fbcbaf25a2cb319 Mon Sep 17 00:00:00 2001 From: zaldih Date: Sat, 10 Jan 2026 13:57:34 +0100 Subject: [PATCH 3/4] docs: remove TTY terminal compatibility issue from known bugs in READMEs --- README.es.md | 1 - README.md | 1 - 2 files changed, 2 deletions(-) diff --git a/README.es.md b/README.es.md index 4fb86f93..4c2f8cac 100644 --- a/README.es.md +++ b/README.es.md @@ -204,7 +204,6 @@ npm run start -- -f -e # :bug: Bugs conocidos :bug: - A veces, el CLI se bloquea mientras un directorio se está borrando. -- Algunas terminales que no utilizan TTY (como git bash en Windows) no funcionan. - La ordenación, especialmente por rutas, puede ralentizar la terminal cuando haya muchos resultados al mismo tiempo. - A veces, los cálculos de tamaño son mayores de lo que deberían ser. - (RESUELTO) Problemas de rendimiento al hacer la búsqueda desde directorios de alto nivel (como / en Linux). diff --git a/README.md b/README.md index 66a6f5fc..8c9fe03c 100644 --- a/README.md +++ b/README.md @@ -279,7 +279,6 @@ You can check the basic API [here](./API.md) or on the web (comming soon). # :bug: Known bugs :bug: - Sometimes, CLI is blocked while folder is deleting. -- Some terminals that do not use TTY (like git bash in windows) do not work. - Sorting, especially by routes, can slow down the terminal when there are many results at the same time. - Sometimes, size calculations are higher than they should be. - (SOLVED) Performance issues when searching from high level directories (like / in linux). From cb8e499d5e4b11034865d20912aae97d2f66564d Mon Sep 17 00:00:00 2001 From: zaldih Date: Sat, 10 Jan 2026 13:59:30 +0100 Subject: [PATCH 4/4] test: add tests for raw mode handling and make mocked TTY properties configurable in --- tests/cli/cli.controller.test.ts | 10 ++++- tests/cli/services/ui.service.test.ts | 59 +++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 tests/cli/services/ui.service.test.ts diff --git a/tests/cli/cli.controller.test.ts b/tests/cli/cli.controller.test.ts index cc0bf4c5..cfa41b74 100644 --- a/tests/cli/cli.controller.test.ts +++ b/tests/cli/cli.controller.test.ts @@ -407,13 +407,19 @@ describe('CliController test', () => { describe('TTY Handling', () => { it('Should run normally even if stdout is NOT TTY', () => { - Object.defineProperty(process.stdout, 'isTTY', { value: false }); + Object.defineProperty(process.stdout, 'isTTY', { + value: false, + configurable: true, + }); cliController.init(); expect(scanSpy).toHaveBeenCalledTimes(1); }); it('Should exit if terminal is too small', () => { - Object.defineProperty(process.stdout, 'columns', { value: 10 }); + Object.defineProperty(process.stdout, 'columns', { + value: 10, + configurable: true, + }); const exitWithErrorSpy = spyMethod('exitWithError'); cliController.init(); expect(exitWithErrorSpy).toHaveBeenCalledTimes(1); diff --git a/tests/cli/services/ui.service.test.ts b/tests/cli/services/ui.service.test.ts new file mode 100644 index 00000000..1a990348 --- /dev/null +++ b/tests/cli/services/ui.service.test.ts @@ -0,0 +1,59 @@ +import { jest } from '@jest/globals'; +import { UiService } from '../../../src/cli/services/ui.service.js'; + +jest.mock('../../../src/dirname.js', () => { + return {}; +}); + +describe('UiService', () => { + let uiService: UiService; + let stdinMock: any; + let stdoutMock: any; + + beforeEach(() => { + stdinMock = { + isTTY: true, + setRawMode: jest.fn(), + resume: jest.fn(), + on: jest.fn(), + }; + stdoutMock = { + write: jest.fn(), + }; + + // Mock process.stdout and process.stdin + Object.defineProperty(process, 'stdin', { + value: stdinMock, + configurable: true, + }); + Object.defineProperty(process, 'stdout', { + value: stdoutMock, + configurable: true, + }); + + uiService = new UiService(); + // Inject the mocked stdin into the service instance as it's assigned in the property declaration + uiService.stdin = stdinMock; + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('setRawMode', () => { + it('should call setRawMode when stdin is TTY', () => { + uiService.setRawMode(true); + expect(stdinMock.setRawMode).toHaveBeenCalledWith(true); + expect(stdinMock.resume).toHaveBeenCalled(); + }); + + it('should NOT call setRawMode when stdin is NOT TTY', () => { + // update mock to simulate non-TTY + stdinMock.isTTY = false; + + uiService.setRawMode(true); + expect(stdinMock.setRawMode).not.toHaveBeenCalled(); + expect(stdinMock.resume).toHaveBeenCalled(); // Resume should still be called + }); + }); +});