Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion README.es.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down
5 changes: 0 additions & 5 deletions src/cli/cli.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
4 changes: 3 additions & 1 deletion src/cli/services/ui.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Comment on lines +10 to +12
Copy link

Copilot AI Jan 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The conditional check for stdin.isTTY before calling setRawMode is a critical behavior change that lacks dedicated test coverage. While the CLI controller tests verify that the application doesn't crash in non-TTY environments, there are no unit tests for the UiService class itself that verify the setRawMode method correctly handles both TTY and non-TTY scenarios. Consider adding a test file tests/cli/services/ui.service.test.ts to directly test this conditional logic.

Copilot uses AI. Check for mistakes.
process.stdin.resume();
}

Expand Down
4 changes: 0 additions & 4 deletions src/constants/messages.constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 ',
Expand Down
31 changes: 29 additions & 2 deletions tests/cli/cli.controller.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down Expand Up @@ -398,5 +404,26 @@ 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,
configurable: true,
});
cliController.init();
expect(scanSpy).toHaveBeenCalledTimes(1);
});

it('Should exit if terminal is too small', () => {
Object.defineProperty(process.stdout, 'columns', {
value: 10,
configurable: true,
});
const exitWithErrorSpy = spyMethod('exitWithError');
cliController.init();
expect(exitWithErrorSpy).toHaveBeenCalledTimes(1);
});
});
});
});
59 changes: 59 additions & 0 deletions tests/cli/services/ui.service.test.ts
Original file line number Diff line number Diff line change
@@ -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
});
});
});