pkg-stats 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- package/README.md +23 -4
- package/dist/__tests__/output.js +8 -0
- package/dist/__tests__/output.test.js +7 -0
- package/dist/cli-options.js +15 -3
- package/dist/colors.js +8 -5
- package/dist/format.js +3 -0
- package/dist/mode/compare-packages.js +11 -17
- package/dist/mode/package-info.js +64 -0
- package/dist/mode/package-stats.js +19 -19
- package/dist/output.js +28 -0
- package/dist/render.js +23 -0
- package/dist/stats.js +8 -3
- package/dist/style.js +16 -0
- package/dist/types.js +1 -0
- package/dist/version.js +0 -3
- package/package.json +1 -1
- package/dist/__tests__/chart.test.js +0 -7
package/README.md
CHANGED
@@ -2,15 +2,34 @@
|
|
2
2
|
|
3
3
|
Beautiful NPM package download stats.
|
4
4
|
|
5
|
+
### Single package
|
6
|
+
|
5
7
|
```
|
6
8
|
npx pkg-stats react
|
7
9
|
```
|
8
10
|
|
9
11
|
<div align='center'>
|
10
|
-
<img src="https://raw.githubusercontent.com/mdjastrzebski/pkg-stats/main/docs/public/example-
|
12
|
+
<img src="https://raw.githubusercontent.com/mdjastrzebski/pkg-stats/main/docs/public/example-package.png" alt="Display single package stats" style="max-width: 610px; aspect-ratio: 610x374;" />
|
13
|
+
</div>
|
14
|
+
|
15
|
+
#### Options:
|
16
|
+
|
17
|
+
- `--major`, `--minor`, `--patch` - group by major, minor or patch version
|
18
|
+
- `--top <number>` (alias `-t`) - show top N versions
|
19
|
+
- `--color <scheme>` (alias `-c`) - specify color scheme
|
20
|
+
- available schemes: `atlas`, `cristal`, `fruit`, `insta`, `mind`, `morning`, `passion`, `pastel`, `rainbow`, `retro`, `summer`, `teen`, `vice`
|
21
|
+
|
22
|
+
|
23
|
+
### Compare packages
|
24
|
+
|
25
|
+
```
|
26
|
+
npx pkg-stats moment date-fns dayjs luxon @js-joda/core
|
27
|
+
```
|
28
|
+
|
29
|
+
<div align='center'>
|
30
|
+
<img src="https://raw.githubusercontent.com/mdjastrzebski/pkg-stats/main/docs/public/example-compare.png" alt="Compare package stats" style="max-width: 610px; aspect-ratio: 610x374;" />
|
11
31
|
</div>
|
12
32
|
|
13
|
-
|
33
|
+
#### Options:
|
14
34
|
|
15
|
-
-
|
16
|
-
- top version: `--top <number>` (alias `-t`)
|
35
|
+
- `--color <scheme>` (alias `-c`) - specify color scheme
|
@@ -0,0 +1,8 @@
|
|
1
|
+
import { expect, test } from 'vitest';
|
2
|
+
import { formatBar } from '../output.js';
|
3
|
+
|
4
|
+
test('renderChart basic tests', () => {
|
5
|
+
expect(formatBar(0.0, { length: 10 })).toMatchInlineSnapshot(`"▏ "`);
|
6
|
+
expect(formatBar(0.5, { length: 10 })).toMatchInlineSnapshot(`"█████ "`);
|
7
|
+
expect(formatBar(1.0, { length: 10 })).toMatchInlineSnapshot(`"██████████"`);
|
8
|
+
});
|
@@ -0,0 +1,7 @@
|
|
1
|
+
import { expect, test } from 'vitest';
|
2
|
+
import { formatBar } from '../output.js';
|
3
|
+
test('renderChart basic tests', () => {
|
4
|
+
expect(formatBar(0.0, { length: 10 })).toMatchInlineSnapshot(`"▏ "`);
|
5
|
+
expect(formatBar(0.5, { length: 10 })).toMatchInlineSnapshot(`"█████ "`);
|
6
|
+
expect(formatBar(1.0, { length: 10 })).toMatchInlineSnapshot(`"██████████"`);
|
7
|
+
});
|
package/dist/cli-options.js
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
import chalk from 'chalk';
|
2
2
|
import meow from 'meow';
|
3
3
|
import redent from 'redent';
|
4
|
-
import { COLOR_SCHEMES } from './colors.js';
|
4
|
+
import { COLOR_SCHEMES, getColorOfDay } from './colors.js';
|
5
5
|
const colorCommand = chalk.hex('#22c1c3');
|
6
6
|
const colorOption = chalk.hex('#fdbb2d');
|
7
7
|
const HELP = `
|
@@ -14,6 +14,7 @@ Options:
|
|
14
14
|
${colorOption('--patch')} Group by patch version
|
15
15
|
${colorOption('-t, --top')} <number> Show top <number> most downloaded versions
|
16
16
|
${colorOption('-a, --all')} Include all versions in output, even those with minimal downloads
|
17
|
+
${colorOption('-x, --extended')} Show extended stats
|
17
18
|
${colorOption('-c, --color')} <scheme> ${wrapOption(`Choose color scheme from: ${COLOR_SCHEMES.sort().join(', ')}`, 50, 24)}
|
18
19
|
|
19
20
|
Examples:
|
@@ -77,6 +78,10 @@ export function parseCliOptions(argv) {
|
|
77
78
|
type: 'boolean',
|
78
79
|
shortFlag: 'a',
|
79
80
|
},
|
81
|
+
extended: {
|
82
|
+
type: 'boolean',
|
83
|
+
shortFlag: 'x',
|
84
|
+
},
|
80
85
|
color: {
|
81
86
|
shortFlag: 'c',
|
82
87
|
type: 'string',
|
@@ -100,7 +105,14 @@ export function parseCliOptions(argv) {
|
|
100
105
|
? 'patch'
|
101
106
|
: undefined,
|
102
107
|
top: cli.flags.top,
|
103
|
-
all: cli.flags.all,
|
104
|
-
|
108
|
+
all: cli.flags.all ?? false,
|
109
|
+
extended: cli.flags.extended ?? false,
|
110
|
+
color: coalesceColor(cli.flags.color) ?? getColorOfDay(),
|
105
111
|
};
|
106
112
|
}
|
113
|
+
function coalesceColor(color) {
|
114
|
+
if (color && COLOR_SCHEMES.includes(color)) {
|
115
|
+
return color;
|
116
|
+
}
|
117
|
+
return undefined;
|
118
|
+
}
|
package/dist/colors.js
CHANGED
@@ -27,17 +27,20 @@ const gradients = {
|
|
27
27
|
summer: { colors: ['#fdbb2d', '#22c1c3'], options: {} },
|
28
28
|
rainbow: {
|
29
29
|
colors: ['#ff0100', '#ff0000'],
|
30
|
-
options: { interpolation: 'hsv', hsvSpin: 'long',
|
30
|
+
options: { interpolation: 'hsv', hsvSpin: 'long', min: 7, extra: 1 },
|
31
31
|
},
|
32
32
|
pastel: {
|
33
33
|
colors: ['#74ebd5', '#74ecd5'],
|
34
|
-
options: { interpolation: 'hsv', hsvSpin: 'long',
|
34
|
+
options: { interpolation: 'hsv', hsvSpin: 'long', extra: 1 },
|
35
35
|
},
|
36
36
|
};
|
37
37
|
export const COLOR_SCHEMES = Object.keys(gradients);
|
38
|
+
export function getPrimaryColor(colorScheme) {
|
39
|
+
return gradients[colorScheme].colors[0];
|
40
|
+
}
|
38
41
|
export function getColors(count, colorScheme) {
|
39
|
-
const { colors, options } = gradients[colorScheme
|
40
|
-
const paddedCount = count + (options.
|
42
|
+
const { colors, options } = gradients[colorScheme];
|
43
|
+
const paddedCount = Math.max(count + (options.extra ?? 0), options.min ?? 0);
|
41
44
|
if (paddedCount < colors.length) {
|
42
45
|
return colors;
|
43
46
|
}
|
@@ -47,7 +50,7 @@ export function getColors(count, colorScheme) {
|
|
47
50
|
: gradient.rgb(paddedCount);
|
48
51
|
return tinyColors.map((c) => c.toHexString());
|
49
52
|
}
|
50
|
-
function getColorOfDay() {
|
53
|
+
export function getColorOfDay() {
|
51
54
|
const date = new Date();
|
52
55
|
const index = date.getDate() + date.getMonth() * 30 + date.getFullYear() * 360;
|
53
56
|
return COLOR_SCHEMES[index % COLOR_SCHEMES.length];
|
package/dist/format.js
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
import chalk from 'chalk';
|
2
|
-
import {
|
3
|
-
import { getColors } from '../colors.js';
|
4
|
-
import { formatDownloads } from '../format.js';
|
2
|
+
import { formatPercentage } from '../format.js';
|
5
3
|
import { fetchNpmLastWeekDownloads } from '../npm-api.js';
|
4
|
+
import { printChart } from '../output.js';
|
6
5
|
export async function comparePackages(packageNames, options) {
|
7
6
|
const rawPackages = await Promise.all(packageNames.map((packageName) => fetchPackageData(packageName)));
|
8
7
|
const packagesToDisplay = rawPackages
|
@@ -13,20 +12,15 @@ export async function comparePackages(packageNames, options) {
|
|
13
12
|
process.exit(1);
|
14
13
|
}
|
15
14
|
console.log(chalk.bold(`\nNPM weekly downloads\n`));
|
16
|
-
const maxDownloads = Math.max(...packagesToDisplay.map((
|
17
|
-
const
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
const maxDownloadsLength = Math.max(...displayData.map((item) => item.downloads.length));
|
26
|
-
const colors = getColors(packagesToDisplay.length, options.color);
|
27
|
-
displayData.forEach((item, i) => {
|
28
|
-
const color = chalk.hex(colors[i]);
|
29
|
-
console.log(`${item.name.padStart(2 + maxNameLength)} ${color(item.chart)} ${color(item.downloads.padStart(maxDownloadsLength))}`);
|
15
|
+
const maxDownloads = Math.max(...packagesToDisplay.map((item) => item.downloads));
|
16
|
+
const items = packagesToDisplay.map((item) => ({
|
17
|
+
label: item.packageName,
|
18
|
+
value: item.downloads,
|
19
|
+
extended: options.extended ? formatPercentage(item.downloads / maxDownloads) : undefined,
|
20
|
+
}));
|
21
|
+
printChart(items, {
|
22
|
+
colorScheme: options.color,
|
23
|
+
indent: 2,
|
30
24
|
});
|
31
25
|
}
|
32
26
|
async function fetchPackageData(packageName) {
|
@@ -0,0 +1,64 @@
|
|
1
|
+
import chalk from 'chalk';
|
2
|
+
import { renderChart } from '../chart.js';
|
3
|
+
import { getColors } from '../colors.js';
|
4
|
+
import { formatDownloads } from '../format.js';
|
5
|
+
import { fetchNpmLastWeekDownloads } from '../npm-api.js';
|
6
|
+
import { filterStats, groupStats } from '../stats.js';
|
7
|
+
import { parseVersion, versionCompare } from '../version.js';
|
8
|
+
export async function fetchPackageInfo(packageName, options) {
|
9
|
+
const data = await fetchNpmLastWeekDownloads(packageName);
|
10
|
+
if (!Object.keys(data.downloads).length) {
|
11
|
+
throw new Error(`No data found for package "${packageName}".`);
|
12
|
+
}
|
13
|
+
const npmStats = Object.keys(data.downloads)
|
14
|
+
.map((versionString) => {
|
15
|
+
const version = parseVersion(versionString);
|
16
|
+
return {
|
17
|
+
...version,
|
18
|
+
downloads: data.downloads[versionString],
|
19
|
+
};
|
20
|
+
})
|
21
|
+
.sort(versionCompare);
|
22
|
+
const { type, stats } = groupStats(npmStats, options.group);
|
23
|
+
const totalDownloads = Object.values(stats).reduce((sum, version) => sum + version.downloads, 0);
|
24
|
+
return {
|
25
|
+
name: packageName,
|
26
|
+
totalDownloads,
|
27
|
+
groupingType: type,
|
28
|
+
stats: stats.map((stat) => ({
|
29
|
+
version: stat.versionString,
|
30
|
+
downloads: stat.downloads,
|
31
|
+
})),
|
32
|
+
};
|
33
|
+
}
|
34
|
+
export function printPackageInfo({ name, stats, totalDownloads, groupingType }, options) {
|
35
|
+
const statsToDisplay = filterStats(stats, {
|
36
|
+
totalDownloads,
|
37
|
+
all: options.all,
|
38
|
+
top: options.top,
|
39
|
+
});
|
40
|
+
const colors = getColors(statsToDisplay.length, options.color);
|
41
|
+
const primaryColor = chalk.hex(colors[0]);
|
42
|
+
console.log(chalk.bold(`\nNPM weekly downloads for ${primaryColor(name)}\n`));
|
43
|
+
console.log(`Total: ${primaryColor(totalDownloads.toLocaleString())} last week\n`);
|
44
|
+
console.log(options.top
|
45
|
+
? `Top ${options.top} ${groupingType} versions:\n`
|
46
|
+
: `By ${groupingType} version:\n`);
|
47
|
+
const maxDownloads = Math.max(...stats.map((v) => v.downloads));
|
48
|
+
const displayData = statsToDisplay.map((item) => {
|
49
|
+
const versionParts = item.version.split('.');
|
50
|
+
return {
|
51
|
+
version: versionParts.length < 3 ? `${item.version}.x` : item.version,
|
52
|
+
chart: renderChart(item.downloads / maxDownloads),
|
53
|
+
downloads: formatDownloads(item.downloads, maxDownloads),
|
54
|
+
};
|
55
|
+
});
|
56
|
+
const maxVersionLength = Math.max(...displayData.map((item) => item.version.length));
|
57
|
+
const maxDownloadsLength = Math.max(...displayData.map((item) => item.downloads.length));
|
58
|
+
displayData.forEach((item, i) => {
|
59
|
+
const color = chalk.hex(colors[i]);
|
60
|
+
console.log(`${item.version.padStart(2 + maxVersionLength)} ${color(item.chart)} ${color(item.downloads.padStart(maxDownloadsLength))}`);
|
61
|
+
});
|
62
|
+
console.log(chalk.bold(`\nNPM weekly downloads for ${chalk.green(name)}\n`));
|
63
|
+
console.log(`Total: ${chalk.green(totalDownloads.toLocaleString())} last week\n`);
|
64
|
+
}
|
@@ -1,8 +1,8 @@
|
|
1
1
|
import chalk from 'chalk';
|
2
|
-
import {
|
3
|
-
import {
|
4
|
-
import { formatDownloads } from '../format.js';
|
2
|
+
import { getPrimaryColor } from '../colors.js';
|
3
|
+
import { formatPercentage } from '../format.js';
|
5
4
|
import { fetchNpmLastWeekDownloads } from '../npm-api.js';
|
5
|
+
import { printChart } from '../output.js';
|
6
6
|
import { filterStats, groupStats } from '../stats.js';
|
7
7
|
import { parseVersion, versionCompare } from '../version.js';
|
8
8
|
export async function printPackageStats(packageName, options) {
|
@@ -34,24 +34,24 @@ export async function printPackageStats(packageName, options) {
|
|
34
34
|
all: options.all,
|
35
35
|
top: options.top,
|
36
36
|
});
|
37
|
-
const
|
38
|
-
|
37
|
+
const downloadToDisplay = statsToDisplay.reduce((sum, version) => sum + version.downloads, 0);
|
38
|
+
if (totalDownloads - downloadToDisplay > 0) {
|
39
|
+
statsToDisplay.push({
|
40
|
+
versionString: 'rest',
|
41
|
+
downloads: totalDownloads - downloadToDisplay,
|
42
|
+
});
|
43
|
+
}
|
44
|
+
const primaryColor = chalk.hex(getPrimaryColor(options.color));
|
39
45
|
console.log(chalk.bold(`\nNPM weekly downloads for ${primaryColor(packageName)}\n`));
|
40
46
|
console.log(`Total: ${primaryColor(totalDownloads.toLocaleString())} last week\n`);
|
41
47
|
console.log(options.top ? `Top ${options.top} ${type} versions:\n` : `By ${type} version:\n`);
|
42
|
-
const
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
});
|
51
|
-
const maxVersionLength = Math.max(...displayData.map((item) => item.version.length));
|
52
|
-
const maxDownloadsLength = Math.max(...displayData.map((item) => item.downloads.length));
|
53
|
-
displayData.forEach((item, i) => {
|
54
|
-
const color = chalk.hex(colors[i]);
|
55
|
-
console.log(`${item.version.padStart(2 + maxVersionLength)} ${color(item.chart)} ${color(item.downloads.padStart(maxDownloadsLength))}`);
|
48
|
+
const items = statsToDisplay.map((item) => ({
|
49
|
+
label: item.versionString,
|
50
|
+
value: item.downloads,
|
51
|
+
extended: options.extended ? formatPercentage(item.downloads / totalDownloads) : undefined,
|
52
|
+
}));
|
53
|
+
printChart(items, {
|
54
|
+
colorScheme: options.color,
|
55
|
+
indent: 2,
|
56
56
|
});
|
57
57
|
}
|
package/dist/output.js
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
import chalk from 'chalk';
|
2
|
+
import { getColors } from './colors.js';
|
3
|
+
import { formatDownloads } from './format.js';
|
4
|
+
export function printChart(items, options) {
|
5
|
+
const maxLabelLength = Math.max(...items.map((item) => item.label.length));
|
6
|
+
const maxValue = Math.max(...items.map((item) => item.value));
|
7
|
+
const maxValueLength = formatDownloads(maxValue, maxValue).length;
|
8
|
+
const maxExtendedLength = Math.max(...items.map((item) => item.extended?.length ?? 0));
|
9
|
+
const colors = getColors(items.length, options.colorScheme);
|
10
|
+
const indent = options.indent ?? 0;
|
11
|
+
items.forEach((item, i) => {
|
12
|
+
const color = chalk.hex(colors[i]);
|
13
|
+
const label = ' '.repeat(indent) + item.label.padStart(maxLabelLength);
|
14
|
+
const bar = formatBar(item.value / maxValue);
|
15
|
+
const value = formatDownloads(item.value, maxValue).padStart(maxValueLength);
|
16
|
+
const extended = item.extended
|
17
|
+
? chalk.dim(` ${item.extended}`.padStart(maxExtendedLength + 1))
|
18
|
+
: '';
|
19
|
+
console.log(`${label} ${color(bar)} ${color(value)}${extended}`);
|
20
|
+
});
|
21
|
+
}
|
22
|
+
export function formatBar(value, { length = 50 } = {}) {
|
23
|
+
const filledChars = Math.round(value * length);
|
24
|
+
if (filledChars === 0) {
|
25
|
+
return '▏' + ' '.repeat(length - 1);
|
26
|
+
}
|
27
|
+
return '█'.repeat(filledChars) + ' '.repeat(length - filledChars);
|
28
|
+
}
|
package/dist/render.js
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
import { style } from './style.js';
|
2
|
+
export function renderChart(value) {
|
3
|
+
const filledValues = Math.round(value * style.chart.width * style.chart.pattern.length);
|
4
|
+
if (filledValues === 0) {
|
5
|
+
return '▏' + (style.rightLabel === 'align-right' ? ' '.repeat(style.chart.width - 1) : '');
|
6
|
+
}
|
7
|
+
const fullBlocks = Math.floor(filledValues / style.chart.pattern.length);
|
8
|
+
const partialBlock = filledValues % style.chart.pattern.length;
|
9
|
+
const hasPartialBlock = partialBlock > 0;
|
10
|
+
return (style.chart.pattern.at(-1).repeat(fullBlocks) +
|
11
|
+
(hasPartialBlock ? style.chart.pattern.at(partialBlock) : '') +
|
12
|
+
(style.rightLabel === 'align-right'
|
13
|
+
? ' '.repeat(style.chart.width - fullBlocks - (hasPartialBlock ? 1 : 0))
|
14
|
+
: ''));
|
15
|
+
}
|
16
|
+
// export type RenderChartLineOptions = {
|
17
|
+
// value: number;
|
18
|
+
// leftLabel: string;
|
19
|
+
// rightLabel: string;
|
20
|
+
// };
|
21
|
+
// export function renderChartLine({ value, leftLabel, rightLabel }: RenderChartLineOptions) {
|
22
|
+
// return `${leftLabel}${renderChart(value)}${rightLabel}`;
|
23
|
+
// }
|
package/dist/stats.js
CHANGED
@@ -22,7 +22,7 @@ export function groupStats(stats, type) {
|
|
22
22
|
function groupByMajor(stats) {
|
23
23
|
const result = {};
|
24
24
|
for (const versionStats of stats) {
|
25
|
-
const key = `${versionStats.major}`;
|
25
|
+
const key = `${versionStats.major}.x`;
|
26
26
|
const entry = result[key] ?? {
|
27
27
|
version: { major: versionStats.major },
|
28
28
|
versionString: key,
|
@@ -36,7 +36,7 @@ function groupByMajor(stats) {
|
|
36
36
|
function groupByMinor(stats) {
|
37
37
|
const result = {};
|
38
38
|
for (const versionStats of stats) {
|
39
|
-
const key = `${versionStats.major}.${versionStats.minor}`;
|
39
|
+
const key = `${versionStats.major}.${versionStats.minor}.x`;
|
40
40
|
const entry = result[key] ?? {
|
41
41
|
version: { major: versionStats.major, minor: versionStats.minor },
|
42
42
|
versionString: key,
|
@@ -73,7 +73,12 @@ export function filterStats(stats, options) {
|
|
73
73
|
return pickTopStats(stats, options.top);
|
74
74
|
}
|
75
75
|
const downloadThreshold = 0.005 * options.totalDownloads; // 0.5%
|
76
|
-
|
76
|
+
const filtered = stats.filter((stat) => stat.downloads >= downloadThreshold);
|
77
|
+
// If we were to skip only a single state, we rather display it than replace it with "rest".
|
78
|
+
if (filtered.length + 1 >= stats.length) {
|
79
|
+
return stats;
|
80
|
+
}
|
81
|
+
return filtered;
|
77
82
|
}
|
78
83
|
function pickTopStats(stats, top) {
|
79
84
|
const sortedStats = stats.sort((a, b) => b.downloads - a.downloads);
|
package/dist/style.js
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
export const style = {
|
2
|
+
chart: {
|
3
|
+
width: 50,
|
4
|
+
//pattern: ['⏹'],
|
5
|
+
//pattern: ['⏺'],
|
6
|
+
//pattern: ['█'],
|
7
|
+
//pattern: [' ', '▎', '▌', '▊', '█'],
|
8
|
+
//pattern: [' ', '▩'],
|
9
|
+
//pattern: [' ', '▰'],
|
10
|
+
//pattern: [' ', '∎'],
|
11
|
+
pattern: [' ', '█'],
|
12
|
+
//pattern: [' ', '▓'],
|
13
|
+
zeroPattern: '▏',
|
14
|
+
},
|
15
|
+
rightLabel: 'align-right',
|
16
|
+
};
|
package/dist/types.js
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
package/dist/version.js
CHANGED
@@ -18,8 +18,5 @@ export function versionCompare(a, b) {
|
|
18
18
|
if (a.patch !== undefined && b.patch !== undefined && a.patch !== b.patch) {
|
19
19
|
return b.patch - a.patch;
|
20
20
|
}
|
21
|
-
if (a.preRelease !== undefined && b.preRelease !== undefined) {
|
22
|
-
return a.preRelease.localeCompare(b.preRelease);
|
23
|
-
}
|
24
21
|
return 0;
|
25
22
|
}
|
package/package.json
CHANGED
@@ -1,7 +0,0 @@
|
|
1
|
-
import { expect, test } from 'vitest';
|
2
|
-
import { renderChart } from '../chart.js';
|
3
|
-
test('renderChart basic tests', () => {
|
4
|
-
expect(renderChart(0.0, { length: 10 })).toMatchInlineSnapshot(`"▏ "`);
|
5
|
-
expect(renderChart(0.5, { length: 10 })).toMatchInlineSnapshot(`"█████ "`);
|
6
|
-
expect(renderChart(1.0, { length: 10 })).toMatchInlineSnapshot(`"██████████"`);
|
7
|
-
});
|