Skip to content

Commit

Permalink
Fix percentage and millisecond rounding
Browse files Browse the repository at this point in the history
  • Loading branch information
spookyuser committed Dec 28, 2023
1 parent 9c1bec7 commit 46d7b74
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 27 deletions.
13 changes: 0 additions & 13 deletions .prettierrc.mjs

This file was deleted.

8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ npm install web-console-progress-bar
## Usage

```js
import { ConsoleProgressBar } from "web-console-progress-bar";
import { ConsoleProgressBar } from "web-console-progress-bar"

const progressBar = new ConsoleProgressBar(100);
progressBar.update(10);
//=> Progress: [██████░░░░░░░░░░░░] 10% ETA: XX.XXs
const progressBar = new ConsoleProgressBar(100)
progressBar.update(10)
//=> Progress: [██████░░░░░░░░░░░░] 10% ETA: XXs
```

## API
Expand Down
27 changes: 23 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"scripts": {
"build": "del-cli dist && tsc",
"prepack": "npm run build",
"test": "tsc --sourceMap && xo"
"test": "tsc --sourceMap && xo && c8 --all node --test --enable-source-maps"
},
"type": "module",
"engines": {
Expand All @@ -36,8 +36,14 @@
}
}
},
"dependencies": {
"ms": "^2.1.3"
},
"devDependencies": {
"@sindresorhus/tsconfig": "^5.0.0",
"c8": "^8.0.1",
"@types/ms": "^0.7.34",
"@types/node": "^20.10.5",
"del-cli": "^5.1.0",
"typescript": "^5.3.3",
"xo": "^0.56.0"
Expand All @@ -47,6 +53,16 @@
"!*.test.*",
"!test.*"
],
"prettier": {
"printWidth": 80,
"tabWidth": 2,
"useTabs": false,
"semi": false,
"singleQuote": false,
"trailingComma": "none",
"bracketSpacing": true,
"bracketSameLine": true
},
"browser": "./dist/index.js",
"types": "./dist/index.d.ts",
"xo": {
Expand All @@ -58,12 +74,15 @@
{
"files": [
"**/*.test.*",
"**/test.*"
"**/test.*",
"**/*.spec.*"
],
"rules": {
"@typescript-eslint/prefer-readonly-parameter-types": "off"
"@typescript-eslint/prefer-readonly-parameter-types": "off",
"@typescript-eslint/no-unsafe-argument": "off",
"@typescript-eslint/dot-notation": "off"
}
}
]
}
}
}
36 changes: 30 additions & 6 deletions source/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import ms from "ms"

/**
* Represents a console progress bar that tracks the progress of a task.
*/
Expand All @@ -16,6 +18,14 @@ export class ConsoleProgressBar {
public total: number,
updateInterval = 2000
) {
if (typeof total !== "number") {
throw new TypeError("total must be a number")
}

if (typeof updateInterval !== "number") {
throw new TypeError("updateInterval must be a number")
}

this.current = 0
this.startTime = performance.now()
this.updateInterval = updateInterval
Expand All @@ -27,6 +37,10 @@ export class ConsoleProgressBar {
* @param currentValue The current value of the progress.
*/
update(currentValue: number) {
if (typeof currentValue !== "number") {
throw new TypeError("currentValue must be a number")
}

const now = performance.now()
if (now - this.lastUpdate < this.updateInterval) {
return
Expand All @@ -36,18 +50,28 @@ export class ConsoleProgressBar {
const percentage = (this.current / this.total) * 100
const elapsedTime = now - this.startTime
const estimatedTotalTime = (elapsedTime / this.current) * this.total
const eta = (estimatedTotalTime - elapsedTime) / 1000 // Seconds
const eta = estimatedTotalTime - elapsedTime

console.clear()
console.log(
`Progress: [${this.renderBar(
percentage
)}] ${percentage}% ETA: ${eta.toFixed(2)}s`
)
console.log(this.generateProgressBarString(percentage, eta))

this.lastUpdate = now
}

/**
* Generates the progress bar string based on the given percentage and ETA.
* @param percentage The percentage of progress completed.
* @param eta The estimated time remaining.
* @returns The progress bar string.
*/
private generateProgressBarString(percentage: number, eta: number) {
const bar = this.renderBar(percentage)
const formattedPercentage = percentage.toFixed(2)
const formattedEta = ms(eta)

return `Progress: [${bar}] ${formattedPercentage}% ETA: ${formattedEta}`
}

/**
* Renders the progress bar based on the given percentage.
* @param percentage The percentage of progress completed.
Expand Down
71 changes: 71 additions & 0 deletions source/test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { strictEqual, throws } from "node:assert"
import { test } from "node:test"
import { ConsoleProgressBar } from "./index.js"

await test("ConsoleProgressBar", async () => {
await test("constructor", async () => {
await test("should throw if total is not a number", async () => {
throws(() => new ConsoleProgressBar("foo" as any))
})

await test("should throw if updateInterval is not a number", async () => {
throws(() => new ConsoleProgressBar(10, "foo" as any))
})

await test("should set the total", async () => {
const progressBar = new ConsoleProgressBar(10)
strictEqual(progressBar.total, 10)
})

await test("should set the updateInterval", async () => {
const progressBar = new ConsoleProgressBar(10, 1000)
strictEqual(progressBar.updateInterval, 1000)
})

await test("should set the default updateInterval", async () => {
const progressBar = new ConsoleProgressBar(10)
strictEqual(progressBar.updateInterval, 2000)
})
})

await test("update", async () => {
await test("should throw if currentValue is not a number", async () => {
const progressBar = new ConsoleProgressBar(10)
throws(() => {
progressBar.update("foo" as any)
})
})

await test("should not update if the interval has not passed", async () => {
const progressBar = new ConsoleProgressBar(10)
progressBar["lastUpdate"] = performance.now()
progressBar.update(5)
strictEqual(
progressBar["lastUpdate"].toFixed(0),
performance.now().toFixed(0)
)
strictEqual(progressBar.current, 0)
})

await test("should update if the interval has passed", async () => {
const progressBar = new ConsoleProgressBar(10)
progressBar["lastUpdate"] = performance.now() - 2000
progressBar.update(5)
strictEqual(progressBar.current, 5)
})
})

await test("renderBar", async () => {
await test("should render the bar", async () => {
const progressBar = new ConsoleProgressBar(10)
const bar = progressBar["renderBar"](50)
strictEqual(bar, "██████████░░░░░░░░░░")
})

await test("should console log the bar", async () => {
const progressBar = new ConsoleProgressBar(10)
const string = progressBar["generateProgressBarString"](50, 2000)
strictEqual(string, "Progress: [██████████░░░░░░░░░░] 50.00% ETA: 2s")
})
})
})

0 comments on commit 46d7b74

Please sign in to comment.