Skip to content

Commit

Permalink
ascii-57: add simple cli
Browse files Browse the repository at this point in the history
  • Loading branch information
gtktsc committed Sep 19, 2023
1 parent 203f8a1 commit e53b1e6
Show file tree
Hide file tree
Showing 12 changed files with 1,624 additions and 437 deletions.
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@ module.exports = {
plugins: ['@typescript-eslint'],
rules: {
'import/extensions': 'off',
'no-param-reassign': 'off',
},
};
9 changes: 8 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@
"description": "Simple ascii chart generator",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"bin": {
"simple-ascii-chart": "dist/cli.js"
},
"scripts": {
"start": "tsc-watch -p tsconfig.json --preserveWatchOutput -w --onSuccess 'node ./dist/index.js'",
"lint": "eslint . --ext .ts,.js",
"lint:fix": "eslint . --ext .ts,.js --fix",
"test": "jest --coverage",
"test:watch": "jest --watch",
"build": "tsc -p tsconfig.build.json",
"build:watch": "tsc -p tsconfig.build.json -w",
"prepare": "husky install",
"prepublishOnly": "npm test && npm run lint",
"preversion": "npm run lint",
Expand Down Expand Up @@ -57,5 +61,8 @@
"homepage": "https://github.com/gtktsc/ascii-chart#readme",
"files": [
"dist/**/*"
]
],
"dependencies": {
"yargs": "^17.7.2"
}
}
137 changes: 137 additions & 0 deletions src/cli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
#!/usr/bin/env node

import * as yargs from 'yargs';
import plot from './index';
import { MultiLine, Settings } from './types';

const { argv } = yargs
.option('input', {
alias: 'i',
type: 'string',
demandOption: true,
})
.option('options', {
alias: 'o',
type: 'string',
description: 'plot settings',
})
.option('height', {
alias: 'h',
type: 'number',
description: 'plot height',
})
.option('hideXAxis', {
type: 'boolean',
description: 'hide x axis',
})
.option('hideYAxis', {
type: 'boolean',
description: 'hide Y axis',
})
.option('fillArea', {
type: 'boolean',
description: 'fill plot area',
})
.option('width', {
alias: 'w',
type: 'number',
description: 'plot width',
})
.option('title', {
alias: 't',
type: 'string',
description: 'plot title',
})
.option('xLabel', {
type: 'string',
description: 'x axis label',
})
.option('color', {
alias: 'c',
type: 'array',
description: 'plot colors',
})
.option('axisCenter', {
type: 'array',
description: 'plot center coordinates',
})
.option('yLabel', {
type: 'string',
description: 'y axis label',
});

const withError = (cb: () => unknown) => {
try {
cb();
} catch (error) {
process.stderr.write('Oops! Something went wrong!\n');
process.exit(1);
}
};

const execute = ({ input, options }: { input: MultiLine; options?: Settings }) => {
withError(() => {
const output = plot(input, options);
process.stdout.write(output);
process.exit(0);
});
};

const prepareParams = ({
input,
options,
width,
height,
hideYAxis,
hideXAxis,
fillArea,
title,
xLabel,
yLabel,
color,
axisCenter,
}: {
input: string;
options?: string;
title?: string;
xLabel?: string;
yLabel?: string;
width?: number;
height?: number;
fillArea?: boolean;
hideYAxis?: boolean;
hideXAxis?: boolean;
color?: (string | number)[];
axisCenter?: (string | number)[];
}) => {
const currentOptions = options ? JSON.parse(options) : {};

return {
input: JSON.parse(input) as MultiLine,
options: {
...currentOptions,
width,
height,
hideYAxis,
hideXAxis,
title,
xLabel,
yLabel,
fillArea,
color,
axisCenter,
},
};
};

if (argv instanceof Promise) {
argv.then((parameters) => {
withError(() => {
execute(prepareParams(parameters));
});
});
} else {
withError(() => {
execute(prepareParams(argv));
});
}
157 changes: 157 additions & 0 deletions src/services/__tests__/defaults.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import { getSymbols, getLabelShift, getInput, getChartSize } from '../defaults';
import { AXIS, EMPTY } from '../../constants';
import { Coordinates, MultiLine } from '../../types';

describe('Chart Helper Functions', () => {
describe('getSymbols', () => {
it('should return default symbols when none are provided', () => {
const symbols = getSymbols({});
expect(symbols).toEqual({
axisSymbols: AXIS,
emptySymbol: EMPTY,
backgroundSymbol: EMPTY,
borderSymbol: undefined,
});
});

it('should override default symbols when provided', () => {
const customSymbols = {
axis: { x: 'X', y: 'Y' },
empty: '-',
background: '=',
border: '#',
};
const symbols = getSymbols({ symbols: customSymbols });
expect(symbols).toEqual({
axisSymbols: { ...AXIS, ...customSymbols.axis },
emptySymbol: customSymbols.empty,
backgroundSymbol: customSymbols.background,
borderSymbol: customSymbols.border,
});
});
});

describe('getChartSize', () => {
it('should return default sizes when width and height are not provided', () => {
const input: MultiLine = [
[
[1, 2],
[2, 4],
[3, 6],
],
];
const size = getChartSize({ input });
expect(size).toEqual({
minX: 1,
plotWidth: 3, // length of rangeX
plotHeight: 5, // maxY - minY + 1
expansionX: [1, 3],
expansionY: [2, 6],
});
});

it('should use provided width and height', () => {
const input: MultiLine = [
[
[1, 2],
[2, 4],
[3, 6],
],
];

const size = getChartSize({ input, width: 10, height: 10 });
expect(size).toEqual({
minX: 1,
plotWidth: 10,
plotHeight: 10,
expansionX: [1, 3],
expansionY: [2, 6],
});
});

it('should adjust for small values without height', () => {
const input: MultiLine = [
[
[1, 2],
[2, 4],
],
];
const size = getChartSize({ input });
expect(size).toEqual({
minX: 1,
plotWidth: 2, // length of rangeX
plotHeight: 3, // length of rangeY since it's less than 3 without provided height
expansionX: [1, 2],
expansionY: [2, 4],
});
});

it('should handle a mix of positive and negative values', () => {
const input: MultiLine = [
[
[-3, -2],
[-2, 4],
[0, 0],
[3, -1],
],
];
const size = getChartSize({ input });
expect(size).toEqual({
minX: -3,
plotWidth: 4, // length of rangeX
plotHeight: 7, // maxY - minY + 1
expansionX: [-3, 3],
expansionY: [-2, 4],
});
});
});

describe('getLabelShift', () => {
it('should calculate label shifts correctly', () => {
const input: MultiLine = [
[
[1, 2],
[3, 4],
[5, 6],
],
];
const transformLabel = (value: number) => value.toString();
const result = getLabelShift({
input,
transformLabel,
expansionX: [1, 5],
expansionY: [2, 6],
minX: 1,
});

expect(result.xShift).toBe(1);
expect(result.yShift).toBe(1);
});
});

describe('getInput', () => {
it('should convert singleline input to multiline', () => {
const input: Coordinates = [
[1, 2],
[3, 4],
];
const result = getInput({ rawInput: input });
expect(result).toEqual([input]);
});

it('should keep multiline input unchanged', () => {
const input: MultiLine = [
[
[1, 2],
[3, 4],
],
[
[5, 6],
[7, 8],
],
];
const result = getInput({ rawInput: input });
expect(result).toEqual(input);
});
});
});
Loading

0 comments on commit e53b1e6

Please sign in to comment.