PluginProbe ʕ •ᴥ•ʔ
Presto Player / trunk
Presto Player vtrunk
4.3.1 4.3.0 4.2.4 4.2.3 4.2.2 4.2.0 4.2.1 trunk 1.10.0 1.10.1 1.10.2 1.11.0 1.12.0 1.13.0 1.14.0 1.14.1 1.5.10 1.5.11 1.5.12 1.5.13 1.5.14 1.5.15 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6.0 1.6.1 1.6.10 1.6.11 1.6.12 1.6.13 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 1.8.0 1.8.1 1.8.2 1.8.3 1.8.4 1.8.5 1.8.6 1.9.0 1.9.1 1.9.10 1.9.11 1.9.12 1.9.13 1.9.14 1.9.2 1.9.3 1.9.4 1.9.5 1.9.6 1.9.7 1.9.8 1.9.9 2.0.0 2.0.1 2.0.10 2.0.11 2.0.12 2.0.13 2.0.14 2.0.15 2.0.16 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9 2.1.0 2.2.0 2.2.1 2.2.2 2.2.3 2.2.3-beta1 2.3.0 2.3.1 2.3.2 2.3.3 3.0.0 3.0.0-beta1 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.0.7 3.0.8 3.1.0 3.1.1 3.1.2 3.1.3 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.0.5 4.0.6 4.0.7 4.0.8 4.1.0 4.1.1 4.1.2 4.1.3 4.1.4
presto-player / src / admin / dashboard / utils / test / formatters.spec.js
presto-player / src / admin / dashboard / utils / test Last commit date
classnames.spec.js 1 month ago dateUtils.spec.js 1 month ago formatters.spec.js 1 month ago pluginUtils.spec.js 1 month ago transformers.spec.js 1 month ago
formatters.spec.js
210 lines
1 import {
2 formatHMS,
3 humanSeconds,
4 humanSecondsCompact,
5 formatChartData,
6 padDailyPoints,
7 decodeHTMLEntities,
8 } from "../formatters";
9
10 describe("formatters", () => {
11 describe("formatHMS", () => {
12 it("returns 00:00:00 for zero", () => {
13 expect(formatHMS(0)).toBe("00:00:00");
14 });
15
16 it("formats sub-minute values with leading zeros", () => {
17 expect(formatHMS(5)).toBe("00:00:05");
18 expect(formatHMS(45)).toBe("00:00:45");
19 });
20
21 it("formats exact-hour values", () => {
22 expect(formatHMS(3600)).toBe("01:00:00");
23 });
24
25 it("formats multi-hour values", () => {
26 expect(formatHMS(3665)).toBe("01:01:05");
27 expect(formatHMS(36000)).toBe("10:00:00");
28 });
29
30 it("rounds fractional seconds", () => {
31 expect(formatHMS(5.4)).toBe("00:00:05");
32 expect(formatHMS(5.6)).toBe("00:00:06");
33 });
34
35 it("treats null and NaN as zero", () => {
36 expect(formatHMS(null)).toBe("00:00:00");
37 expect(formatHMS(NaN)).toBe("00:00:00");
38 expect(formatHMS(undefined)).toBe("00:00:00");
39 });
40 });
41
42 describe("humanSeconds", () => {
43 it("returns 0s for zero / null / undefined", () => {
44 expect(humanSeconds(0)).toBe("0s");
45 expect(humanSeconds(null)).toBe("0s");
46 expect(humanSeconds(undefined)).toBe("0s");
47 });
48
49 it("returns seconds-only when under a minute", () => {
50 expect(humanSeconds(45)).toBe("45s");
51 });
52
53 it("returns minutes + seconds when under an hour", () => {
54 expect(humanSeconds(65)).toBe("1m 5s");
55 expect(humanSeconds(120)).toBe("2m");
56 });
57
58 it("returns hours + minutes + seconds for multi-hour values", () => {
59 expect(humanSeconds(3665)).toBe("1h 1m 5s");
60 });
61
62 it("includes hour-only when minutes/seconds are zero", () => {
63 expect(humanSeconds(7200)).toBe("2h");
64 });
65 });
66
67 describe("humanSecondsCompact", () => {
68 it("stays in seconds up to (but not including) 90s", () => {
69 expect(humanSecondsCompact(0)).toBe("0s");
70 expect(humanSecondsCompact(89)).toBe("89s");
71 });
72
73 it("switches to minutes at 90s and up to 90m", () => {
74 expect(humanSecondsCompact(90)).toBe("2m");
75 expect(humanSecondsCompact(60 * 60)).toBe("60m");
76 });
77
78 it("switches to hours at 90 minutes", () => {
79 expect(humanSecondsCompact(90 * 60)).toBe("2h");
80 expect(humanSecondsCompact(7200)).toBe("2h");
81 });
82
83 it("coerces non-numeric input to 0s", () => {
84 expect(humanSecondsCompact("not-a-number")).toBe("0s");
85 expect(humanSecondsCompact(null)).toBe("0s");
86 expect(humanSecondsCompact(undefined)).toBe("0s");
87 });
88 });
89
90 // Local-time formatter so tests stay deterministic regardless of host TZ
91 // (the helpers under test compute keys via local-time getters).
92 const fmt = (d, _pattern) => {
93 const y = d.getFullYear();
94 const m = String(d.getMonth() + 1).padStart(2, "0");
95 const day = String(d.getDate()).padStart(2, "0");
96 return `${y}-${m}-${day}`;
97 };
98
99 describe("formatChartData", () => {
100 it("returns an empty array for empty / null input", () => {
101 expect(formatChartData([], "total", false, fmt)).toEqual([]);
102 expect(formatChartData(null, "total", false, fmt)).toEqual([]);
103 });
104
105 it("sorts ascending by date_time and maps to { month, count }", () => {
106 const data = [
107 { date_time: "2026-01-03T12:00:00", total: "5" },
108 { date_time: "2026-01-01T12:00:00", total: "1" },
109 { date_time: "2026-01-02T12:00:00", total: "3" },
110 ];
111 const out = formatChartData(data, "total", false, fmt);
112 expect(out.map((p) => p.month)).toEqual([
113 "2026-01-01",
114 "2026-01-02",
115 "2026-01-03",
116 ]);
117 expect(out.map((p) => p.count)).toEqual([1, 3, 5]);
118 });
119
120 it("divides by 60 and rounds to 2 dp when isWatchTime=true", () => {
121 const data = [{ date_time: "2026-01-01T12:00:00", total: "150" }];
122 const out = formatChartData(data, "total", true, fmt);
123 expect(out[0].count).toBe(2.5);
124 });
125
126 it("falls back to 0 for non-numeric values via parseInt", () => {
127 const data = [{ date_time: "2026-01-01T12:00:00", total: "n/a" }];
128 const out = formatChartData(data, "total", false, fmt);
129 expect(out[0].count).toBe(0);
130 });
131 });
132
133 describe("padDailyPoints", () => {
134 it("returns input unchanged when range is missing or incomplete", () => {
135 const points = [{ month: "2026-01-01", count: 1 }];
136 expect(padDailyPoints(points, undefined, fmt)).toBe(points);
137 expect(padDailyPoints(points, { from: null, to: null }, fmt)).toBe(
138 points
139 );
140 });
141
142 it("returns input unchanged when formatDate is missing", () => {
143 const points = [{ month: "2026-01-01", count: 1 }];
144 expect(
145 padDailyPoints(points, {
146 from: new Date(2026, 0, 1),
147 to: new Date(2026, 0, 3),
148 })
149 ).toBe(points);
150 });
151
152 it("fills missing days with zero counts and preserves existing counts", () => {
153 const points = [
154 { month: "2026-01-01", count: 5 },
155 { month: "2026-01-03", count: 7 },
156 ];
157 const range = {
158 from: new Date(2026, 0, 1, 10),
159 to: new Date(2026, 0, 3, 20),
160 };
161 const out = padDailyPoints(points, range, fmt);
162 expect(out).toEqual([
163 { month: "2026-01-01", count: 5 },
164 { month: "2026-01-02", count: 0 },
165 { month: "2026-01-03", count: 7 },
166 ]);
167 });
168
169 it("is inclusive of both range endpoints", () => {
170 const range = {
171 from: new Date(2026, 0, 1),
172 to: new Date(2026, 0, 1),
173 };
174 const out = padDailyPoints([], range, fmt);
175 expect(out).toHaveLength(1);
176 expect(out[0].count).toBe(0);
177 });
178
179 it("treats null/undefined points as empty when range is given", () => {
180 const range = {
181 from: new Date(2026, 0, 1),
182 to: new Date(2026, 0, 2),
183 };
184 const out = padDailyPoints(null, range, fmt);
185 expect(out).toEqual([
186 { month: "2026-01-01", count: 0 },
187 { month: "2026-01-02", count: 0 },
188 ]);
189 });
190 });
191
192 describe("decodeHTMLEntities", () => {
193 it("returns empty string for falsy input", () => {
194 expect(decodeHTMLEntities("")).toBe("");
195 expect(decodeHTMLEntities(null)).toBe("");
196 expect(decodeHTMLEntities(undefined)).toBe("");
197 });
198
199 it("decodes numeric entities like '", () => {
200 expect(decodeHTMLEntities("John's Video")).toBe("John's Video");
201 });
202
203 it("decodes named entities like &", () => {
204 expect(decodeHTMLEntities("Sales & Marketing")).toBe(
205 "Sales & Marketing"
206 );
207 });
208 });
209 });
210