From 764af70f8e14eaa5aa15472edc2add3e10b36be3 Mon Sep 17 00:00:00 2001 From: AlexisMora Date: Mon, 22 Dec 2025 15:20:56 +0100 Subject: [PATCH 1/2] fix: command upload folder now asks for folder path if no folder flag provided --- .nvmrc | 1 + src/commands/upload-folder.ts | 31 ++++++++++--- test/commands/upload-folder.test.ts | 72 ++++++++++++++++++++++++++++- 3 files changed, 96 insertions(+), 8 deletions(-) create mode 100644 .nvmrc diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..2bd5a0a --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +22 diff --git a/src/commands/upload-folder.ts b/src/commands/upload-folder.ts index cb00895..c810cd8 100644 --- a/src/commands/upload-folder.ts +++ b/src/commands/upload-folder.ts @@ -4,6 +4,7 @@ import { AuthService } from '../services/auth.service'; import { ValidationService } from '../services/validation.service'; import { ConfigService } from '../services/config.service'; import { UploadFacade } from '../services/network/upload/upload-facade.service'; +import { NotValidDirectoryError } from '../types/command.types'; export default class UploadFolder extends Command { static readonly args = {}; @@ -15,7 +16,7 @@ export default class UploadFolder extends Command { folder: Flags.string({ char: 'f', description: 'The path to the folder on your system.', - required: true, + required: false, }), destination: Flags.string({ char: 'i', @@ -29,10 +30,7 @@ export default class UploadFolder extends Command { public run = async () => { const { user } = await AuthService.instance.getAuthDetails(); const { flags } = await this.parse(UploadFolder); - const doesDirectoryExist = await ValidationService.instance.validateDirectoryExists(flags['folder']); - if (!doesDirectoryExist) { - throw new Error(`The provided folder path is not a valid directory: ${flags['folder']}`); - } + const localPath = await this.getFolderPath(flags['folder'], flags['non-interactive']); // If destinationFolderUuid is empty from flags&prompt, means we should use RootFolderUuid const destinationFolderUuid = @@ -52,7 +50,7 @@ export default class UploadFolder extends Command { ); progressBar?.start(100, 0); const data = await UploadFacade.instance.uploadFolder({ - localPath: flags['folder'], + localPath, destinationFolderUuid, loginUserDetails: user, jsonFlag: flags['json'], @@ -80,4 +78,25 @@ export default class UploadFolder extends Command { }); this.exit(1); }; + + private getFolderPath = async (folderFlag: string | undefined, nonInteractive: boolean): Promise => { + return await CLIUtils.getValueFromFlag( + { + value: folderFlag, + name: UploadFolder.flags['folder'].name, + }, + { + nonInteractive, + prompt: { + message: 'What is the path to the folder on your computer?', + options: { type: 'input' }, + }, + }, + { + validate: ValidationService.instance.validateDirectoryExists, + error: new NotValidDirectoryError(), + }, + this.log.bind(this), + ); + }; } diff --git a/test/commands/upload-folder.test.ts b/test/commands/upload-folder.test.ts index abd6af7..02b44b1 100644 --- a/test/commands/upload-folder.test.ts +++ b/test/commands/upload-folder.test.ts @@ -4,7 +4,7 @@ import { AuthService } from '../../src/services/auth.service'; import { LoginCredentials, MissingCredentialsError } from '../../src/types/command.types'; import { ValidationService } from '../../src/services/validation.service'; import { UserFixture } from '../fixtures/auth.fixture'; -import { CLIUtils } from '../../src/utils/cli.utils'; +import { CLIUtils, NoFlagProvidedError } from '../../src/utils/cli.utils'; import { UploadResult } from '../../src/services/network/upload/upload.types'; import { UploadFacade } from '../../src/services/network/upload/upload-facade.service'; @@ -107,7 +107,7 @@ describe('Upload Folder Command', () => { .mockResolvedValue(false); const invalidPath = '/invalid/folder/path.txt'; - const result = UploadFolder.run([`--folder=${invalidPath}`]); + const result = UploadFolder.run([`--folder=${invalidPath}`, '--non-interactive']); await expect(result).rejects.toMatchObject({ message: expect.stringContaining('EEXIT: 1'), @@ -134,4 +134,72 @@ describe('Upload Folder Command', () => { expect(validateDirectoryExistsSpy).not.toHaveBeenCalled(); expect(UploadFacadeSpy).not.toHaveBeenCalled(); }); + + describe('Folder path resolution (getFolderPath)', () => { + it('should prompt user for folder path in interactive mode when --folder flag is not provided', async () => { + const getValueFromFlagSpy = vi.spyOn(CLIUtils, 'getValueFromFlag').mockResolvedValue('/prompted/folder/path'); + + await UploadFolder.run([]); + + expect(getValueFromFlagSpy).toHaveBeenCalledWith( + { value: undefined, name: 'folder' }, + expect.objectContaining({ + prompt: { + message: 'What is the path to the folder on your computer?', + options: { type: 'input' }, + }, + }), + expect.objectContaining({ + validate: ValidationService.instance.validateDirectoryExists, + }), + expect.any(Function), + ); + expect(UploadFacadeSpy).toHaveBeenCalledWith( + expect.objectContaining({ + localPath: '/prompted/folder/path', + }), + ); + }); + + it('should throw NoFlagProvidedError in non-interactive mode when --folder flag is not provided', async () => { + const getValueFromFlagSpy = vi + .spyOn(CLIUtils, 'getValueFromFlag') + .mockRejectedValue(new NoFlagProvidedError('folder')); + + const result = UploadFolder.run(['--non-interactive']); + + await expect(result).rejects.toMatchObject({ + message: expect.stringContaining('EEXIT: 1'), + oclif: { exit: 1 }, + }); + + expect(getValueFromFlagSpy).toHaveBeenCalledWith( + { value: undefined, name: 'folder' }, + { + nonInteractive: true, + prompt: { + message: 'What is the path to the folder on your computer?', + options: { type: 'input' }, + }, + }, + { + validate: ValidationService.instance.validateDirectoryExists, + error: expect.any(Error), + }, + expect.any(Function), + ); + expect(UploadFacadeSpy).not.toHaveBeenCalled(); + }); + + it('should use folder path from --folder flag when provided', async () => { + await UploadFolder.run(['--folder=/explicit/folder/path']); + + expect(validateDirectoryExistsSpy).toHaveBeenCalledWith('/explicit/folder/path'); + expect(UploadFacadeSpy).toHaveBeenCalledWith( + expect.objectContaining({ + localPath: '/explicit/folder/path', + }), + ); + }); + }); }); From 6047dcdefe7fbb9347540d0df13e4b32772f1a12 Mon Sep 17 00:00:00 2001 From: AlexisMora Date: Mon, 22 Dec 2025 16:14:51 +0100 Subject: [PATCH 2/2] chore:remove .nvmrc --- .gitignore | 4 +++- .nvmrc | 1 - 2 files changed, 3 insertions(+), 2 deletions(-) delete mode 100644 .nvmrc diff --git a/.gitignore b/.gitignore index 24ea642..e9691d2 100644 --- a/.gitignore +++ b/.gitignore @@ -19,4 +19,6 @@ cli.inxt .vscode # Claude Code project instructions -CLAUDE.md \ No newline at end of file +CLAUDE.md + +.nvmrc \ No newline at end of file diff --git a/.nvmrc b/.nvmrc deleted file mode 100644 index 2bd5a0a..0000000 --- a/.nvmrc +++ /dev/null @@ -1 +0,0 @@ -22