UNPKG

rs-runner

Version:

RS is a CLI tool for quickly detecting package.json scripts, and running them.

452 lines (365 loc) 13.9 kB
import { output } from '../src/lib/output'; import * as config from '../src/lib/config'; import { getGlobalScripts, getDirectoryScripts, addNewGlobalScript, removeGlobalScript, addNewDirectoryScript, removeDirectoryScript, validateScript, } from '../src/lib/scripts'; import { Config } from '../src/types'; jest.mock('../src/lib/config'); jest.mock('../src/lib/output'); describe('scripts module', () => { const mockCwd = '/home/user/project'; beforeEach(() => { jest.resetAllMocks(); jest.spyOn(process, 'cwd').mockReturnValue(mockCwd); }); afterEach(() => { jest.restoreAllMocks(); }); describe('getGlobalScripts', () => { it('should return global scripts from config', () => { const mockConfig: Config = { globalScripts: { lint: 'eslint .', format: 'prettier --write .', }, directoryScripts: {}, }; (config.getConfig as jest.Mock).mockReturnValue(mockConfig); expect(getGlobalScripts()).toEqual(mockConfig.globalScripts); }); it('should return empty object when config is null', () => { (config.getConfig as jest.Mock).mockReturnValue(null); expect(getGlobalScripts()).toEqual({}); }); it('should return empty object when globalScripts is undefined', () => { (config.getConfig as jest.Mock).mockReturnValue({ directoryScripts: {}, }); expect(getGlobalScripts()).toEqual({}); }); }); describe('getDirectoryScripts', () => { it('should return directory scripts for current directory', () => { const mockConfig: Config = { globalScripts: {}, directoryScripts: { [mockCwd]: { dev: 'vite dev', build: 'vite build', }, }, }; (config.getConfig as jest.Mock).mockReturnValue(mockConfig); expect(getDirectoryScripts()).toEqual(mockConfig.directoryScripts[mockCwd]); }); it('should return empty object when config is null', () => { (config.getConfig as jest.Mock).mockReturnValue(null); expect(getDirectoryScripts()).toEqual({}); }); it('should return empty object when directoryScripts is undefined', () => { (config.getConfig as jest.Mock).mockReturnValue({ globalScripts: {}, }); expect(getDirectoryScripts()).toEqual({}); }); it('should return empty object when current directory has no scripts', () => { const mockConfig: Config = { globalScripts: {}, directoryScripts: { '/other/directory': { test: 'jest' }, }, }; (config.getConfig as jest.Mock).mockReturnValue(mockConfig); expect(getDirectoryScripts()).toEqual({}); }); }); describe('addNewGlobalScript', () => { it('should add new global script to empty config', () => { (config.getConfig as jest.Mock) .mockReturnValueOnce(null) .mockReturnValueOnce({ globalScripts: { test: 'jest' }, directoryScripts: {}, }); (config.writeConfig as jest.Mock).mockReturnValue(true); addNewGlobalScript('test', 'jest'); expect(config.writeConfig).toHaveBeenCalledTimes(1); expect(config.writeConfig).toHaveBeenCalledWith({ globalScripts: { test: 'jest' }, directoryScripts: {}, }); expect(output).toHaveBeenCalledWith( "Global script 'test' added successfully.", 'green', ); }); it('should add new global script to existing config', () => { const existingConfig: Config = { globalScripts: { lint: 'eslint .' }, directoryScripts: {}, }; (config.getConfig as jest.Mock) .mockReturnValueOnce(existingConfig) .mockReturnValueOnce({ globalScripts: { lint: 'eslint .', test: 'jest' }, directoryScripts: {}, }); (config.writeConfig as jest.Mock).mockReturnValue(true); addNewGlobalScript('test', 'jest'); expect(config.writeConfig).toHaveBeenCalledWith({ globalScripts: { lint: 'eslint .', test: 'jest' }, directoryScripts: {}, }); }); it('should warn when overwriting existing script', () => { const existingConfig: Config = { globalScripts: { test: 'mocha' }, directoryScripts: {}, }; (config.getConfig as jest.Mock) .mockReturnValueOnce(existingConfig) .mockReturnValueOnce({ globalScripts: { test: 'jest' }, directoryScripts: {}, }); (config.writeConfig as jest.Mock).mockReturnValue(true); addNewGlobalScript('test', 'jest'); expect(output.warn).toHaveBeenCalledWith( "Global script 'test' already exists. Overwriting...", ); }); it('should show error when write fails', () => { (config.getConfig as jest.Mock) .mockReturnValueOnce(null) .mockReturnValueOnce(null); (config.writeConfig as jest.Mock).mockReturnValue(true); addNewGlobalScript('test', 'jest'); expect(output.error).toHaveBeenCalledWith( 'Failed to verify script was saved correctly.', ); }); it('should not continue when writeConfig returns false', () => { (config.getConfig as jest.Mock).mockReturnValueOnce(null); (config.writeConfig as jest.Mock).mockReturnValue(false); addNewGlobalScript('test', 'jest'); expect(config.writeConfig).toHaveBeenCalledTimes(1); // getConfig should not be called a second time when write fails expect(config.getConfig).toHaveBeenCalledTimes(1); }); }); describe('removeGlobalScript', () => { it('should remove existing global script', () => { const existingConfig: Config = { globalScripts: { test: 'jest', lint: 'eslint .' }, directoryScripts: {}, }; (config.getConfig as jest.Mock).mockReturnValue(existingConfig); (config.writeConfig as jest.Mock).mockReturnValue(true); removeGlobalScript('test'); expect(config.writeConfig).toHaveBeenCalledTimes(1); expect(config.writeConfig).toHaveBeenCalledWith({ globalScripts: { lint: 'eslint .' }, directoryScripts: {}, }); expect(output).toHaveBeenCalledWith( "Global script 'test' removed successfully.", 'green', ); }); it('should warn when config is null', () => { (config.getConfig as jest.Mock).mockReturnValue(null); removeGlobalScript('test'); expect(output.warn).toHaveBeenCalledWith( 'No global scripts configuration found.', ); expect(config.writeConfig).not.toHaveBeenCalled(); }); it('should warn when script does not exist and show available', () => { const existingConfig: Config = { globalScripts: { lint: 'eslint .' }, directoryScripts: {}, }; (config.getConfig as jest.Mock).mockReturnValue(existingConfig); removeGlobalScript('nonexistent'); expect(output.warn).toHaveBeenCalledWith( "Global script 'nonexistent' not found. Available: lint", ); expect(config.writeConfig).not.toHaveBeenCalled(); }); }); describe('addNewDirectoryScript', () => { it('should add new directory script to empty config', () => { (config.getConfig as jest.Mock) .mockReturnValueOnce(null) .mockReturnValueOnce({ globalScripts: {}, directoryScripts: { [mockCwd]: { dev: 'vite dev' }, }, }); (config.writeConfig as jest.Mock).mockReturnValue(true); addNewDirectoryScript('dev', 'vite dev'); expect(config.writeConfig).toHaveBeenCalledTimes(1); expect(config.writeConfig).toHaveBeenCalledWith({ globalScripts: {}, directoryScripts: { [mockCwd]: { dev: 'vite dev' }, }, }); }); it('should add to existing directory scripts', () => { const existingConfig: Config = { globalScripts: {}, directoryScripts: { [mockCwd]: { build: 'vite build' }, }, }; (config.getConfig as jest.Mock) .mockReturnValueOnce(existingConfig) .mockReturnValueOnce({ globalScripts: {}, directoryScripts: { [mockCwd]: { build: 'vite build', dev: 'vite dev' }, }, }); (config.writeConfig as jest.Mock).mockReturnValue(true); addNewDirectoryScript('dev', 'vite dev'); expect(config.writeConfig).toHaveBeenCalledWith({ globalScripts: {}, directoryScripts: { [mockCwd]: { build: 'vite build', dev: 'vite dev' }, }, }); }); it('should warn when overwriting existing directory script', () => { const existingConfig: Config = { globalScripts: {}, directoryScripts: { [mockCwd]: { dev: 'old command' }, }, }; (config.getConfig as jest.Mock) .mockReturnValueOnce(existingConfig) .mockReturnValueOnce({ globalScripts: {}, directoryScripts: { [mockCwd]: { dev: 'vite dev' }, }, }); (config.writeConfig as jest.Mock).mockReturnValue(true); addNewDirectoryScript('dev', 'vite dev'); expect(output.warn).toHaveBeenCalledWith( `Directory script 'dev' already exists for ${mockCwd}. Overwriting...`, ); }); it('should show error when write fails', () => { (config.getConfig as jest.Mock) .mockReturnValueOnce(null) .mockReturnValueOnce(null); (config.writeConfig as jest.Mock).mockReturnValue(true); addNewDirectoryScript('dev', 'vite dev'); expect(output.error).toHaveBeenCalledWith( 'Failed to verify script was saved correctly.', ); }); it('should not continue when writeConfig returns false', () => { (config.getConfig as jest.Mock).mockReturnValueOnce(null); (config.writeConfig as jest.Mock).mockReturnValue(false); addNewDirectoryScript('dev', 'vite dev'); expect(config.writeConfig).toHaveBeenCalledTimes(1); expect(config.getConfig).toHaveBeenCalledTimes(1); }); }); describe('removeDirectoryScript', () => { it('should remove existing directory script', () => { const existingConfig: Config = { globalScripts: {}, directoryScripts: { [mockCwd]: { dev: 'vite dev', build: 'vite build' }, }, }; (config.getConfig as jest.Mock).mockReturnValue(existingConfig); (config.writeConfig as jest.Mock).mockReturnValue(true); removeDirectoryScript('dev'); expect(config.writeConfig).toHaveBeenCalledTimes(1); expect(config.writeConfig).toHaveBeenCalledWith({ globalScripts: {}, directoryScripts: { [mockCwd]: { build: 'vite build' }, }, }); expect(output).toHaveBeenCalledWith( "Directory script 'dev' removed successfully.", 'green', ); }); it('should clean up empty directory entries', () => { const existingConfig: Config = { globalScripts: {}, directoryScripts: { [mockCwd]: { dev: 'vite dev' }, }, }; (config.getConfig as jest.Mock).mockReturnValue(existingConfig); (config.writeConfig as jest.Mock).mockReturnValue(true); removeDirectoryScript('dev'); expect(config.writeConfig).toHaveBeenCalledWith({ globalScripts: {}, directoryScripts: {}, }); }); it('should warn when no directory scripts exist', () => { (config.getConfig as jest.Mock).mockReturnValue(null); removeDirectoryScript('dev'); expect(output.warn).toHaveBeenCalledWith( 'No directory scripts found for current directory.', ); }); it('should warn when script does not exist and show available', () => { const existingConfig: Config = { globalScripts: {}, directoryScripts: { [mockCwd]: { build: 'vite build' }, }, }; (config.getConfig as jest.Mock).mockReturnValue(existingConfig); removeDirectoryScript('nonexistent'); expect(output.warn).toHaveBeenCalledWith( "Directory script 'nonexistent' not found. Available: build", ); }); }); describe('validateScript', () => { it('should return valid for normal commands', () => { const result = validateScript('npm test'); expect(result.valid).toBe(true); expect(result.warnings).toHaveLength(0); }); it('should return invalid for empty command', () => { const result = validateScript(''); expect(result.valid).toBe(false); expect(result.warnings).toContain('Script command cannot be empty'); }); it('should return invalid for whitespace-only command', () => { const result = validateScript(' '); expect(result.valid).toBe(false); expect(result.warnings).toContain('Script command cannot be empty'); }); it('should warn about dangerous rm -rf /', () => { const result = validateScript('echo test; rm -rf /'); expect(result.valid).toBe(true); expect(result.warnings).toContain('Warning: dangerous rm -rf /'); }); it('should allow safe rm commands', () => { const result = validateScript('rm -rf ./node_modules'); expect(result.valid).toBe(true); expect(result.warnings).toHaveLength(0); }); }); });