rs-runner
Version:
RS is a CLI tool for quickly detecting package.json scripts, and running them.
159 lines (123 loc) • 4.51 kB
text/typescript
import * as fs from 'fs';
import path from 'path';
import { detectRunner, getPackageJsonScripts } from '../src/lib/pm';
jest.mock('fs');
describe('pm module', () => {
beforeEach(() => {
jest.resetAllMocks();
});
describe('detectRunner', () => {
it('should detect npm from package-lock.json', () => {
(fs.existsSync as jest.Mock).mockImplementation(
(file) => file === 'package-lock.json',
);
expect(detectRunner()).toBe('npm');
});
it('should detect yarn from yarn.lock', () => {
(fs.existsSync as jest.Mock).mockImplementation(
(file) => file === 'yarn.lock',
);
expect(detectRunner()).toBe('yarn');
});
it('should detect pnpm from pnpm-lock.yaml', () => {
(fs.existsSync as jest.Mock).mockImplementation(
(file) => file === 'pnpm-lock.yaml',
);
expect(detectRunner()).toBe('pnpm');
});
it('should detect bun from bun.lockb', () => {
(fs.existsSync as jest.Mock).mockImplementation(
(file) => file === 'bun.lockb',
);
expect(detectRunner()).toBe('bun');
});
it('should detect bun from bun.lock', () => {
(fs.existsSync as jest.Mock).mockImplementation(
(file) => file === 'bun.lock',
);
expect(detectRunner()).toBe('bun');
});
it('should detect deno from deno.lock', () => {
(fs.existsSync as jest.Mock).mockImplementation(
(file) => file === 'deno.lock',
);
expect(detectRunner()).toBe('deno');
});
it('should return null when no lock file exists', () => {
(fs.existsSync as jest.Mock).mockReturnValue(false);
expect(detectRunner()).toBeNull();
});
it('should prioritize first matching runner (npm over others)', () => {
(fs.existsSync as jest.Mock).mockImplementation(
(file) =>
file === 'package-lock.json' ||
file === 'yarn.lock' ||
file === 'pnpm-lock.yaml',
);
expect(detectRunner()).toBe('npm');
});
});
describe('getPackageJsonScripts', () => {
it('should return scripts from package.json', () => {
const mockPackageJson = {
scripts: {
test: 'jest',
build: 'tsc',
dev: 'tsc -w',
},
};
(fs.existsSync as jest.Mock).mockReturnValue(true);
(fs.readFileSync as jest.Mock).mockReturnValue(
JSON.stringify(mockPackageJson),
);
const result = getPackageJsonScripts();
expect(result).toEqual(mockPackageJson.scripts);
expect(fs.readFileSync).toHaveBeenCalledWith(
path.join(process.cwd(), 'package.json'),
'utf8',
);
});
it('should return empty object when package.json does not exist', () => {
(fs.existsSync as jest.Mock).mockReturnValue(false);
expect(getPackageJsonScripts()).toEqual({});
});
it('should return empty object when package.json has no scripts', () => {
const mockPackageJson = {
name: 'test-package',
version: '1.0.0',
};
(fs.existsSync as jest.Mock).mockReturnValue(true);
(fs.readFileSync as jest.Mock).mockReturnValue(
JSON.stringify(mockPackageJson),
);
expect(getPackageJsonScripts()).toEqual({});
});
it('should throw SyntaxError for malformed JSON', () => {
(fs.existsSync as jest.Mock).mockReturnValue(true);
(fs.readFileSync as jest.Mock).mockReturnValue('{ invalid json }');
expect(() => getPackageJsonScripts()).toThrow(SyntaxError);
});
it('should return empty object when scripts is not an object', () => {
const mockPackageJson = {
name: 'test-package',
scripts: ['not', 'an', 'object'],
};
(fs.existsSync as jest.Mock).mockReturnValue(true);
(fs.readFileSync as jest.Mock).mockReturnValue(
JSON.stringify(mockPackageJson),
);
expect(getPackageJsonScripts()).toEqual({});
});
it('should return empty object when scripts is null', () => {
const mockPackageJson = {
name: 'test-package',
scripts: null,
};
(fs.existsSync as jest.Mock).mockReturnValue(true);
(fs.readFileSync as jest.Mock).mockReturnValue(
JSON.stringify(mockPackageJson),
);
expect(getPackageJsonScripts()).toEqual({});
});
});
});