@hobenakicoffee/libraries 3.4.0 → 3.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -2,20 +2,55 @@ import { describe, expect, test } from "bun:test";
|
|
|
2
2
|
import { formatCount } from "./format-count";
|
|
3
3
|
|
|
4
4
|
describe("formatCount", () => {
|
|
5
|
-
test("formats
|
|
5
|
+
test("formats zero and small numbers without suffix", () => {
|
|
6
6
|
expect(formatCount(0)).toBe("0");
|
|
7
|
+
expect(formatCount(7)).toBe("7");
|
|
7
8
|
expect(formatCount(999)).toBe("999");
|
|
8
9
|
});
|
|
9
10
|
|
|
10
|
-
test("
|
|
11
|
+
test("handles negative numbers", () => {
|
|
12
|
+
expect(formatCount(-1000)).toBe("-1k");
|
|
13
|
+
expect(formatCount(-1500)).toBe("-1.5k");
|
|
14
|
+
expect(formatCount(-1_000_000)).toBe("-1M");
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test("formats thousands with k suffix", () => {
|
|
11
18
|
expect(formatCount(1000)).toBe("1k");
|
|
12
|
-
expect(formatCount(2000)).toBe("2k");
|
|
13
19
|
expect(formatCount(10_000)).toBe("10k");
|
|
14
|
-
expect(formatCount(
|
|
20
|
+
expect(formatCount(100_000)).toBe("100k");
|
|
15
21
|
});
|
|
16
22
|
|
|
17
23
|
test("shows decimal for non-round thousands", () => {
|
|
18
24
|
expect(formatCount(1500)).toBe("1.5k");
|
|
19
25
|
expect(formatCount(10_500)).toBe("10.5k");
|
|
26
|
+
expect(formatCount(15_500)).toBe("15.5k");
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test("formats millions with M suffix", () => {
|
|
30
|
+
expect(formatCount(1_000_000)).toBe("1M");
|
|
31
|
+
expect(formatCount(10_000_000)).toBe("10M");
|
|
32
|
+
expect(formatCount(100_000_000)).toBe("100M");
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("shows decimal for non-round millions", () => {
|
|
36
|
+
expect(formatCount(1_500_000)).toBe("1.5M");
|
|
37
|
+
expect(formatCount(10_500_000)).toBe("10.5M");
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test("formats billions with B suffix", () => {
|
|
41
|
+
expect(formatCount(1_000_000_000)).toBe("1B");
|
|
42
|
+
expect(formatCount(10_000_000_000)).toBe("10B");
|
|
43
|
+
expect(formatCount(100_000_000_000)).toBe("100B");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("shows decimal for non-round billions", () => {
|
|
47
|
+
expect(formatCount(1_500_000_000)).toBe("1.5B");
|
|
48
|
+
expect(formatCount(2_100_000_000)).toBe("2.1B");
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("uses whole numbers for large round values", () => {
|
|
52
|
+
expect(formatCount(1000)).toBe("1k");
|
|
53
|
+
expect(formatCount(1_000_000)).toBe("1M");
|
|
54
|
+
expect(formatCount(1_000_000_000)).toBe("1B");
|
|
20
55
|
});
|
|
21
56
|
});
|
|
@@ -1,11 +1,18 @@
|
|
|
1
|
-
|
|
1
|
+
export function formatCount(n: number): string {
|
|
2
|
+
const abs = Math.abs(n);
|
|
3
|
+
const sign = n < 0 ? "-" : "";
|
|
2
4
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
?
|
|
8
|
-
|
|
9
|
-
}
|
|
10
|
-
|
|
5
|
+
const format = (divisor: number, suffix: string) => {
|
|
6
|
+
const val = abs / divisor;
|
|
7
|
+
// Show one decimal only if it's meaningful (e.g. 1.2k), not for whole numbers or large values
|
|
8
|
+
const formatted =
|
|
9
|
+
val >= 100 || Number.isInteger(val) ? Math.round(val) : val.toFixed(1);
|
|
10
|
+
return `${sign}${formatted}${suffix}`;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
if (abs >= 1_000_000_000) return format(1_000_000_000, "B");
|
|
14
|
+
if (abs >= 1_000_000) return format(1_000_000, "M");
|
|
15
|
+
if (abs >= 1000) return format(1000, "k");
|
|
16
|
+
|
|
17
|
+
return `${sign}${abs}`;
|
|
11
18
|
}
|
|
@@ -5,7 +5,7 @@ describe("getProductLink", () => {
|
|
|
5
5
|
test("builds product link with default baseUrl", () => {
|
|
6
6
|
const result = getProductLink("johndoe", "my-product");
|
|
7
7
|
expect(result).toBe(
|
|
8
|
-
"https://hobenakicoffee.com/@johndoe/
|
|
8
|
+
"https://hobenakicoffee.com/@johndoe/shop/products/my-product"
|
|
9
9
|
);
|
|
10
10
|
});
|
|
11
11
|
|
|
@@ -15,22 +15,20 @@ describe("getProductLink", () => {
|
|
|
15
15
|
"my-product",
|
|
16
16
|
"https://custom.com"
|
|
17
17
|
);
|
|
18
|
-
expect(result).toBe(
|
|
19
|
-
"https://custom.com/@johndoe/shops/products/my-product"
|
|
20
|
-
);
|
|
18
|
+
expect(result).toBe("https://custom.com/@johndoe/shop/products/my-product");
|
|
21
19
|
});
|
|
22
20
|
|
|
23
21
|
test("sanitizes username with whitespace", () => {
|
|
24
22
|
const result = getProductLink(" john doe ", "my-product");
|
|
25
23
|
expect(result).toBe(
|
|
26
|
-
"https://hobenakicoffee.com/@johndoe/
|
|
24
|
+
"https://hobenakicoffee.com/@johndoe/shop/products/my-product"
|
|
27
25
|
);
|
|
28
26
|
});
|
|
29
27
|
|
|
30
28
|
test("does not encode slug - passes through as-is", () => {
|
|
31
29
|
const result = getProductLink("johndoe", "my product 123");
|
|
32
30
|
expect(result).toBe(
|
|
33
|
-
"https://hobenakicoffee.com/@johndoe/
|
|
31
|
+
"https://hobenakicoffee.com/@johndoe/shop/products/my-product-123"
|
|
34
32
|
);
|
|
35
33
|
});
|
|
36
34
|
});
|
|
@@ -6,5 +6,5 @@ export function getProductLink(
|
|
|
6
6
|
baseUrl = "https://hobenakicoffee.com"
|
|
7
7
|
) {
|
|
8
8
|
const sanitizedSlug = slug.trim().replace(/\s+/g, "-");
|
|
9
|
-
return `${getUserPageLink(username, baseUrl)}/
|
|
9
|
+
return `${getUserPageLink(username, baseUrl)}/shop/products/${sanitizedSlug}`;
|
|
10
10
|
}
|