@simplysm/core-common 13.0.69 → 13.0.71

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.
Files changed (151) hide show
  1. package/README.md +66 -267
  2. package/dist/common.types.d.ts +14 -14
  3. package/dist/errors/argument-error.d.ts +10 -10
  4. package/dist/errors/argument-error.d.ts.map +1 -1
  5. package/dist/errors/argument-error.js +2 -2
  6. package/dist/errors/argument-error.js.map +1 -1
  7. package/dist/errors/not-implemented-error.d.ts +8 -8
  8. package/dist/errors/not-implemented-error.js +2 -2
  9. package/dist/errors/not-implemented-error.js.map +1 -1
  10. package/dist/errors/sd-error.d.ts +10 -10
  11. package/dist/errors/sd-error.d.ts.map +1 -1
  12. package/dist/errors/timeout-error.d.ts +10 -10
  13. package/dist/errors/timeout-error.js +3 -3
  14. package/dist/errors/timeout-error.js.map +1 -1
  15. package/dist/extensions/arr-ext.d.ts +2 -2
  16. package/dist/extensions/arr-ext.helpers.d.ts +8 -8
  17. package/dist/extensions/arr-ext.helpers.js +1 -1
  18. package/dist/extensions/arr-ext.helpers.js.map +1 -1
  19. package/dist/extensions/arr-ext.js +13 -13
  20. package/dist/extensions/arr-ext.js.map +1 -1
  21. package/dist/extensions/arr-ext.types.d.ts +57 -57
  22. package/dist/extensions/arr-ext.types.d.ts.map +1 -1
  23. package/dist/extensions/map-ext.d.ts +16 -16
  24. package/dist/extensions/set-ext.d.ts +11 -11
  25. package/dist/features/debounce-queue.d.ts +17 -15
  26. package/dist/features/debounce-queue.d.ts.map +1 -1
  27. package/dist/features/debounce-queue.js +6 -6
  28. package/dist/features/debounce-queue.js.map +1 -1
  29. package/dist/features/event-emitter.d.ts +20 -20
  30. package/dist/features/event-emitter.js +17 -17
  31. package/dist/features/serial-queue.d.ts +11 -11
  32. package/dist/features/serial-queue.js +5 -5
  33. package/dist/features/serial-queue.js.map +1 -1
  34. package/dist/globals.d.ts +4 -4
  35. package/dist/types/date-only.d.ts +64 -64
  36. package/dist/types/date-only.d.ts.map +1 -1
  37. package/dist/types/date-only.js +63 -63
  38. package/dist/types/date-time.d.ts +37 -37
  39. package/dist/types/date-time.d.ts.map +1 -1
  40. package/dist/types/date-time.js +54 -37
  41. package/dist/types/date-time.js.map +1 -1
  42. package/dist/types/lazy-gc-map.d.ts +26 -26
  43. package/dist/types/lazy-gc-map.d.ts.map +1 -1
  44. package/dist/types/lazy-gc-map.js +26 -26
  45. package/dist/types/lazy-gc-map.js.map +1 -1
  46. package/dist/types/time.d.ts +25 -25
  47. package/dist/types/time.d.ts.map +1 -1
  48. package/dist/types/time.js +25 -25
  49. package/dist/types/time.js.map +1 -1
  50. package/dist/types/uuid.d.ts +11 -11
  51. package/dist/types/uuid.d.ts.map +1 -1
  52. package/dist/types/uuid.js +12 -12
  53. package/dist/types/uuid.js.map +1 -1
  54. package/dist/utils/bytes.d.ts +17 -17
  55. package/dist/utils/bytes.js +4 -4
  56. package/dist/utils/bytes.js.map +1 -1
  57. package/dist/utils/date-format.d.ts +45 -45
  58. package/dist/utils/date-format.js +1 -1
  59. package/dist/utils/date-format.js.map +1 -1
  60. package/dist/utils/error.d.ts +4 -4
  61. package/dist/utils/json.d.ts +17 -17
  62. package/dist/utils/json.js +3 -3
  63. package/dist/utils/json.js.map +1 -1
  64. package/dist/utils/num.d.ts +23 -23
  65. package/dist/utils/obj.d.ts +111 -111
  66. package/dist/utils/obj.d.ts.map +1 -1
  67. package/dist/utils/obj.js +3 -3
  68. package/dist/utils/obj.js.map +1 -1
  69. package/dist/utils/path.d.ts +10 -10
  70. package/dist/utils/primitive.d.ts +5 -5
  71. package/dist/utils/primitive.js +1 -1
  72. package/dist/utils/primitive.js.map +1 -1
  73. package/dist/utils/str.d.ts +46 -46
  74. package/dist/utils/str.d.ts.map +1 -1
  75. package/dist/utils/str.js +5 -5
  76. package/dist/utils/str.js.map +1 -1
  77. package/dist/utils/template-strings.d.ts +26 -26
  78. package/dist/utils/transferable.d.ts +18 -18
  79. package/dist/utils/transferable.js +1 -1
  80. package/dist/utils/transferable.js.map +1 -1
  81. package/dist/utils/wait.d.ts +9 -9
  82. package/dist/utils/xml.d.ts +13 -13
  83. package/dist/utils/xml.d.ts.map +1 -1
  84. package/dist/utils/xml.js +1 -0
  85. package/dist/utils/xml.js.map +1 -1
  86. package/dist/zip/sd-zip.d.ts +22 -22
  87. package/dist/zip/sd-zip.js +16 -16
  88. package/package.json +4 -4
  89. package/src/common.types.ts +17 -17
  90. package/src/errors/argument-error.ts +15 -15
  91. package/src/errors/not-implemented-error.ts +9 -9
  92. package/src/errors/sd-error.ts +12 -12
  93. package/src/errors/timeout-error.ts +12 -12
  94. package/src/extensions/arr-ext.helpers.ts +10 -10
  95. package/src/extensions/arr-ext.ts +57 -57
  96. package/src/extensions/arr-ext.types.ts +59 -59
  97. package/src/extensions/map-ext.ts +16 -16
  98. package/src/extensions/set-ext.ts +11 -11
  99. package/src/features/debounce-queue.ts +21 -19
  100. package/src/features/event-emitter.ts +25 -25
  101. package/src/features/serial-queue.ts +13 -13
  102. package/src/globals.ts +4 -4
  103. package/src/index.ts +1 -1
  104. package/src/types/date-only.ts +83 -83
  105. package/src/types/date-time.ts +64 -44
  106. package/src/types/lazy-gc-map.ts +45 -45
  107. package/src/types/time.ts +34 -34
  108. package/src/types/uuid.ts +17 -17
  109. package/src/utils/bytes.ts +35 -35
  110. package/src/utils/date-format.ts +65 -65
  111. package/src/utils/error.ts +4 -4
  112. package/src/utils/json.ts +39 -39
  113. package/src/utils/num.ts +23 -23
  114. package/src/utils/obj.ts +138 -138
  115. package/src/utils/path.ts +10 -10
  116. package/src/utils/primitive.ts +6 -6
  117. package/src/utils/str.ts +260 -261
  118. package/src/utils/template-strings.ts +29 -29
  119. package/src/utils/transferable.ts +284 -284
  120. package/src/utils/wait.ts +10 -10
  121. package/src/utils/xml.ts +20 -19
  122. package/src/zip/sd-zip.ts +25 -25
  123. package/tests/errors/errors.spec.ts +80 -0
  124. package/tests/extensions/array-extension.spec.ts +796 -0
  125. package/tests/extensions/map-extension.spec.ts +147 -0
  126. package/tests/extensions/set-extension.spec.ts +74 -0
  127. package/tests/types/date-only.spec.ts +638 -0
  128. package/tests/types/date-time.spec.ts +391 -0
  129. package/tests/types/lazy-gc-map.spec.ts +692 -0
  130. package/tests/types/time.spec.ts +559 -0
  131. package/tests/types/uuid.spec.ts +74 -0
  132. package/tests/utils/bytes-utils.spec.ts +230 -0
  133. package/tests/utils/date-format.spec.ts +373 -0
  134. package/tests/utils/debounce-queue.spec.ts +272 -0
  135. package/tests/utils/json.spec.ts +486 -0
  136. package/tests/utils/number.spec.ts +157 -0
  137. package/tests/utils/object.spec.ts +829 -0
  138. package/tests/utils/path.spec.ts +78 -0
  139. package/tests/utils/primitive.spec.ts +43 -0
  140. package/tests/utils/sd-event-emitter.spec.ts +216 -0
  141. package/tests/utils/serial-queue.spec.ts +365 -0
  142. package/tests/utils/string.spec.ts +281 -0
  143. package/tests/utils/template-strings.spec.ts +57 -0
  144. package/tests/utils/transferable.spec.ts +703 -0
  145. package/tests/utils/wait.spec.ts +145 -0
  146. package/tests/utils/xml.spec.ts +146 -0
  147. package/tests/zip/sd-zip.spec.ts +238 -0
  148. package/docs/extensions.md +0 -503
  149. package/docs/features.md +0 -109
  150. package/docs/types.md +0 -486
  151. package/docs/utils.md +0 -780
@@ -0,0 +1,559 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { Time } from "@simplysm/core-common";
3
+
4
+ describe("Time", () => {
5
+ //#region Constructor
6
+
7
+ describe("constructor", () => {
8
+ it("Returns current time when created without arguments", () => {
9
+ const time = new Time();
10
+
11
+ // Time changes in real-time so range test
12
+ expect(time.hour).toBeGreaterThanOrEqual(0);
13
+ expect(time.hour).toBeLessThanOrEqual(23);
14
+ expect(time.minute).toBeGreaterThanOrEqual(0);
15
+ expect(time.minute).toBeLessThanOrEqual(59);
16
+ });
17
+
18
+ it("Creates with hour, minute, second", () => {
19
+ const time = new Time(15, 30, 45);
20
+
21
+ expect(time.hour).toBe(15);
22
+ expect(time.minute).toBe(30);
23
+ expect(time.second).toBe(45);
24
+ expect(time.millisecond).toBe(0);
25
+ });
26
+
27
+ it("Creates with hour, minute, second, millisecond", () => {
28
+ const time = new Time(15, 30, 45, 123);
29
+
30
+ expect(time.hour).toBe(15);
31
+ expect(time.minute).toBe(30);
32
+ expect(time.second).toBe(45);
33
+ expect(time.millisecond).toBe(123);
34
+ });
35
+
36
+ it("Creates with tick (millisecond)", () => {
37
+ // 15:30:45.123 = (15*60*60 + 30*60 + 45)*1000 + 123
38
+ const tick = (15 * 60 * 60 + 30 * 60 + 45) * 1000 + 123;
39
+ const time = new Time(tick);
40
+
41
+ expect(time.hour).toBe(15);
42
+ expect(time.minute).toBe(30);
43
+ expect(time.second).toBe(45);
44
+ expect(time.millisecond).toBe(123);
45
+ });
46
+
47
+ it("Creates with Date type", () => {
48
+ const date = new Date(2025, 0, 6, 15, 30, 45, 123);
49
+ const time = new Time(date);
50
+
51
+ expect(time.hour).toBe(15);
52
+ expect(time.minute).toBe(30);
53
+ expect(time.second).toBe(45);
54
+ expect(time.millisecond).toBe(123);
55
+ });
56
+
57
+ it("Normalizes to 24 hours if exceeds", () => {
58
+ // 25 hours = 1 hour
59
+ const time = new Time(25, 0, 0);
60
+
61
+ expect(time.hour).toBe(1);
62
+ });
63
+
64
+ it("Normalizes negative hours/minutes/seconds to within 24 hours", () => {
65
+ // -1 hour 30 minutes = 23 hours 30 minutes (24 - 0.5 = 23.5)
66
+ const time = new Time(-1, 30, 0);
67
+
68
+ expect(time.hour).toBe(23);
69
+ expect(time.minute).toBe(30);
70
+ });
71
+
72
+ it("Normalizes negative tick to within 24 hours", () => {
73
+ // -1 hour = -3600000ms → 23 hours
74
+ const time = new Time(-3600000);
75
+
76
+ expect(time.hour).toBe(23);
77
+ expect(time.minute).toBe(0);
78
+ expect(time.second).toBe(0);
79
+ });
80
+
81
+ it("Negative tick (-1ms) normalizes to 23:59:59.999", () => {
82
+ const time = new Time(-1);
83
+
84
+ expect(time.hour).toBe(23);
85
+ expect(time.minute).toBe(59);
86
+ expect(time.second).toBe(59);
87
+ expect(time.millisecond).toBe(999);
88
+ });
89
+ });
90
+
91
+ //#endregion
92
+
93
+ //#region parse
94
+
95
+ describe("parse()", () => {
96
+ it("Parses HH:mm:ss format", () => {
97
+ const time = Time.parse("15:30:45");
98
+
99
+ expect(time.hour).toBe(15);
100
+ expect(time.minute).toBe(30);
101
+ expect(time.second).toBe(45);
102
+ expect(time.millisecond).toBe(0);
103
+ });
104
+
105
+ it("Parses HH:mm:ss.fff format", () => {
106
+ const time = Time.parse("15:30:45.123");
107
+
108
+ expect(time.hour).toBe(15);
109
+ expect(time.minute).toBe(30);
110
+ expect(time.second).toBe(45);
111
+ expect(time.millisecond).toBe(123);
112
+ });
113
+
114
+ it("Parses AM HH:mm:ss format", () => {
115
+ const time = Time.parse("AM 9:30:45");
116
+
117
+ expect(time.hour).toBe(9);
118
+ expect(time.minute).toBe(30);
119
+ expect(time.second).toBe(45);
120
+ });
121
+
122
+ it("Parses PM HH:mm:ss format", () => {
123
+ const time = Time.parse("PM 3:30:45");
124
+
125
+ expect(time.hour).toBe(15); // 12 + 3
126
+ expect(time.minute).toBe(30);
127
+ expect(time.second).toBe(45);
128
+ });
129
+
130
+ it("Pads milliseconds with 0 if insufficient digits", () => {
131
+ const time = Time.parse("15:30:45.1");
132
+
133
+ expect(time.millisecond).toBe(100); // '1' → '100'
134
+ });
135
+
136
+ it("Throws error for invalid format", () => {
137
+ expect(() => Time.parse("invalid-time")).toThrow();
138
+ });
139
+
140
+ it("PM 12:00:00 is noon (12 o'clock)", () => {
141
+ const time = Time.parse("PM 12:00:00");
142
+
143
+ expect(time.hour).toBe(12);
144
+ expect(time.minute).toBe(0);
145
+ expect(time.second).toBe(0);
146
+ });
147
+
148
+ it("AM 12:00:00 is midnight (0 o'clock)", () => {
149
+ const time = Time.parse("AM 12:00:00");
150
+
151
+ expect(time.hour).toBe(0);
152
+ expect(time.minute).toBe(0);
153
+ expect(time.second).toBe(0);
154
+ });
155
+
156
+ it("PM 12:30:45 is after noon (12:30:45)", () => {
157
+ const time = Time.parse("PM 12:30:45");
158
+
159
+ expect(time.hour).toBe(12);
160
+ expect(time.minute).toBe(30);
161
+ expect(time.second).toBe(45);
162
+ });
163
+
164
+ it("AM 12:30:45 is after midnight (0:30:45)", () => {
165
+ const time = Time.parse("AM 12:30:45");
166
+
167
+ expect(time.hour).toBe(0);
168
+ expect(time.minute).toBe(30);
169
+ expect(time.second).toBe(45);
170
+ });
171
+
172
+ it("Parses time from ISO 8601 format (UTC -> local conversion)", () => {
173
+ // UTC time is converted to local time
174
+ const time = Time.parse("2025-01-15T10:30:45Z");
175
+ const expected = new Date("2025-01-15T10:30:45Z");
176
+
177
+ expect(time.hour).toBe(expected.getHours());
178
+ expect(time.minute).toBe(30);
179
+ expect(time.second).toBe(45);
180
+ });
181
+
182
+ it("Parses milliseconds from ISO 8601 format (UTC -> local conversion)", () => {
183
+ // UTC time is converted to local time
184
+ const time = Time.parse("2025-01-15T10:30:45.123Z");
185
+ const expected = new Date("2025-01-15T10:30:45.123Z");
186
+
187
+ expect(time.hour).toBe(expected.getHours());
188
+ expect(time.minute).toBe(30);
189
+ expect(time.second).toBe(45);
190
+ expect(time.millisecond).toBe(123);
191
+ });
192
+ });
193
+
194
+ //#endregion
195
+
196
+ //#region Getters
197
+
198
+ describe("Getters", () => {
199
+ it("Returns hour", () => {
200
+ const time = new Time(15, 30, 45, 123);
201
+ expect(time.hour).toBe(15);
202
+ });
203
+
204
+ it("Returns minute", () => {
205
+ const time = new Time(15, 30, 45, 123);
206
+ expect(time.minute).toBe(30);
207
+ });
208
+
209
+ it("Returns second", () => {
210
+ const time = new Time(15, 30, 45, 123);
211
+ expect(time.second).toBe(45);
212
+ });
213
+
214
+ it("Returns millisecond", () => {
215
+ const time = new Time(15, 30, 45, 123);
216
+ expect(time.millisecond).toBe(123);
217
+ });
218
+
219
+ it("Returns tick", () => {
220
+ const time = new Time(15, 30, 45, 123);
221
+ const expectedTick = (15 * 60 * 60 + 30 * 60 + 45) * 1000 + 123;
222
+ expect(time.tick).toBe(expectedTick);
223
+ });
224
+ });
225
+
226
+ //#endregion
227
+
228
+ //#region tick comparison
229
+
230
+ describe("tick comparison", () => {
231
+ it("Same times have same tick", () => {
232
+ const t1 = new Time(10, 30, 45, 123);
233
+ const t2 = new Time(10, 30, 45, 123);
234
+
235
+ expect(t1.tick).toBe(t2.tick);
236
+ });
237
+
238
+ it("Different times have different ticks", () => {
239
+ const t1 = new Time(10, 30, 45, 123);
240
+ const t2 = new Time(10, 30, 45, 124);
241
+
242
+ expect(t1.tick).not.toBe(t2.tick);
243
+ });
244
+
245
+ it("Can compare time order by tick", () => {
246
+ const t1 = new Time(0, 0, 0);
247
+ const t2 = new Time(12, 30, 0);
248
+ const t3 = new Time(23, 59, 59);
249
+
250
+ expect(t1.tick).toBeLessThan(t2.tick);
251
+ expect(t2.tick).toBeLessThan(t3.tick);
252
+ });
253
+
254
+ it("Millisecond precision comparison is possible", () => {
255
+ const t1 = new Time(10, 30, 45, 0);
256
+ const t2 = new Time(10, 30, 45, 1);
257
+
258
+ expect(t2.tick - t1.tick).toBe(1);
259
+ });
260
+
261
+ it("Can calculate time difference by tick", () => {
262
+ const t1 = new Time(10, 0, 0);
263
+ const t2 = new Time(11, 0, 0);
264
+
265
+ // 1 hour = 3600000ms
266
+ expect(t2.tick - t1.tick).toBe(3600000);
267
+ });
268
+ });
269
+
270
+ //#endregion
271
+
272
+ //#region setX methods (immutable)
273
+
274
+ describe("setHour()", () => {
275
+ it("Returns new instance with hour changed", () => {
276
+ const time = new Time(15, 30, 45, 123);
277
+ const newTime = time.setHour(20);
278
+
279
+ expect(newTime.hour).toBe(20);
280
+ expect(newTime.minute).toBe(30);
281
+ expect(newTime.second).toBe(45);
282
+ expect(newTime.millisecond).toBe(123);
283
+ expect(time.hour).toBe(15); // original immutable
284
+ });
285
+ });
286
+
287
+ describe("setMinute()", () => {
288
+ it("Returns new instance with minute changed", () => {
289
+ const time = new Time(15, 30, 45, 123);
290
+ const newTime = time.setMinute(50);
291
+
292
+ expect(newTime.hour).toBe(15);
293
+ expect(newTime.minute).toBe(50);
294
+ expect(newTime.second).toBe(45);
295
+ expect(newTime.millisecond).toBe(123);
296
+ expect(time.minute).toBe(30); // original immutable
297
+ });
298
+ });
299
+
300
+ describe("setSecond()", () => {
301
+ it("Returns new instance with second changed", () => {
302
+ const time = new Time(15, 30, 45, 123);
303
+ const newTime = time.setSecond(55);
304
+
305
+ expect(newTime.hour).toBe(15);
306
+ expect(newTime.minute).toBe(30);
307
+ expect(newTime.second).toBe(55);
308
+ expect(newTime.millisecond).toBe(123);
309
+ expect(time.second).toBe(45); // original immutable
310
+ });
311
+ });
312
+
313
+ describe("setMillisecond()", () => {
314
+ it("Returns new instance with millisecond changed", () => {
315
+ const time = new Time(15, 30, 45, 123);
316
+ const newTime = time.setMillisecond(456);
317
+
318
+ expect(newTime.hour).toBe(15);
319
+ expect(newTime.minute).toBe(30);
320
+ expect(newTime.second).toBe(45);
321
+ expect(newTime.millisecond).toBe(456);
322
+ expect(time.millisecond).toBe(123); // original immutable
323
+ });
324
+ });
325
+
326
+ //#endregion
327
+
328
+ //#region addX methods (immutable)
329
+
330
+ describe("addHours()", () => {
331
+ it("Adds positive hours", () => {
332
+ const time = new Time(15, 30, 45);
333
+ const newTime = time.addHours(3);
334
+
335
+ expect(newTime.hour).toBe(18);
336
+ expect(newTime.minute).toBe(30);
337
+ expect(newTime.second).toBe(45);
338
+ });
339
+
340
+ it("Adds negative hours (subtraction)", () => {
341
+ const time = new Time(15, 30, 45);
342
+ const newTime = time.addHours(-5);
343
+
344
+ expect(newTime.hour).toBe(10);
345
+ });
346
+
347
+ it("Keeps remainder if exceeds 24 hours", () => {
348
+ const time = new Time(22, 0, 0);
349
+ const newTime = time.addHours(5);
350
+
351
+ expect(newTime.hour).toBe(3); // (22 + 5) % 24
352
+ });
353
+ });
354
+
355
+ describe("addMinutes()", () => {
356
+ it("Adds positive minutes", () => {
357
+ const time = new Time(15, 30, 45);
358
+ const newTime = time.addMinutes(20);
359
+
360
+ expect(newTime.hour).toBe(15);
361
+ expect(newTime.minute).toBe(50);
362
+ });
363
+
364
+ it("Adds negative minutes (subtraction)", () => {
365
+ const time = new Time(15, 30, 45);
366
+ const newTime = time.addMinutes(-20);
367
+
368
+ expect(newTime.minute).toBe(10);
369
+ });
370
+
371
+ it("Increases hour if exceeds 60 minutes", () => {
372
+ const time = new Time(15, 50, 0);
373
+ const newTime = time.addMinutes(20);
374
+
375
+ expect(newTime.hour).toBe(16);
376
+ expect(newTime.minute).toBe(10);
377
+ });
378
+ });
379
+
380
+ describe("addSeconds()", () => {
381
+ it("Adds positive seconds", () => {
382
+ const time = new Time(15, 30, 45);
383
+ const newTime = time.addSeconds(10);
384
+
385
+ expect(newTime.hour).toBe(15);
386
+ expect(newTime.minute).toBe(30);
387
+ expect(newTime.second).toBe(55);
388
+ });
389
+
390
+ it("Adds negative seconds (subtraction)", () => {
391
+ const time = new Time(15, 30, 45);
392
+ const newTime = time.addSeconds(-10);
393
+
394
+ expect(newTime.second).toBe(35);
395
+ });
396
+
397
+ it("Increases minute if exceeds 60 seconds", () => {
398
+ const time = new Time(15, 30, 50);
399
+ const newTime = time.addSeconds(20);
400
+
401
+ expect(newTime.minute).toBe(31);
402
+ expect(newTime.second).toBe(10);
403
+ });
404
+ });
405
+
406
+ describe("addMilliseconds()", () => {
407
+ it("Adds positive milliseconds", () => {
408
+ const time = new Time(15, 30, 45, 100);
409
+ const newTime = time.addMilliseconds(50);
410
+
411
+ expect(newTime.millisecond).toBe(150);
412
+ });
413
+
414
+ it("Adds negative milliseconds (subtraction)", () => {
415
+ const time = new Time(15, 30, 45, 100);
416
+ const newTime = time.addMilliseconds(-50);
417
+
418
+ expect(newTime.millisecond).toBe(50);
419
+ });
420
+
421
+ it("Increases second if exceeds 1000 milliseconds", () => {
422
+ const time = new Time(15, 30, 45, 900);
423
+ const newTime = time.addMilliseconds(200);
424
+
425
+ expect(newTime.second).toBe(46);
426
+ expect(newTime.millisecond).toBe(100);
427
+ });
428
+ });
429
+
430
+ //#endregion
431
+
432
+ //#region Negative operations (24-hour boundary handling)
433
+
434
+ describe("Negative operations (24-hour boundary)", () => {
435
+ it("addHours(-25) is same time yesterday (23 hours ago)", () => {
436
+ // Subtracting 25 hours from 10:00 = previous day 9:00 = 24 - 25 + 10 = 9:00
437
+ const time = new Time(10, 0, 0);
438
+ const newTime = time.addHours(-25);
439
+
440
+ expect(newTime.hour).toBe(9);
441
+ expect(newTime.minute).toBe(0);
442
+ expect(newTime.second).toBe(0);
443
+ });
444
+
445
+ it("addHours(-10) crosses midnight to previous day", () => {
446
+ // Subtracting 10 hours from 5:00 = 19:00
447
+ const time = new Time(5, 0, 0);
448
+ const newTime = time.addHours(-10);
449
+
450
+ expect(newTime.hour).toBe(19);
451
+ });
452
+
453
+ it("addMinutes(-90) is 1 hour 30 minutes ago", () => {
454
+ // Subtracting 90 minutes from 1:30 = 0:00
455
+ const time = new Time(1, 30, 0);
456
+ const newTime = time.addMinutes(-90);
457
+
458
+ expect(newTime.hour).toBe(0);
459
+ expect(newTime.minute).toBe(0);
460
+ });
461
+
462
+ it("addMinutes(-90) crosses midnight to previous day", () => {
463
+ // Subtracting 90 minutes from 0:30 = previous day 23:00
464
+ const time = new Time(0, 30, 0);
465
+ const newTime = time.addMinutes(-90);
466
+
467
+ expect(newTime.hour).toBe(23);
468
+ expect(newTime.minute).toBe(0);
469
+ });
470
+
471
+ it("addSeconds(-3700) is about 1 hour ago", () => {
472
+ // Subtracting 3700 seconds (1 hour 1 minute 40 seconds) from 1:00:00 = 23:58:20
473
+ const time = new Time(1, 0, 0);
474
+ const newTime = time.addSeconds(-3700);
475
+
476
+ expect(newTime.hour).toBe(23);
477
+ expect(newTime.minute).toBe(58);
478
+ expect(newTime.second).toBe(20);
479
+ });
480
+
481
+ it("addMilliseconds(-1000) crosses midnight to previous day", () => {
482
+ // Subtracting 1000ms from 0:00:00.500ms = previous day 23:59:59.500ms
483
+ const time = new Time(0, 0, 0, 500);
484
+ const newTime = time.addMilliseconds(-1000);
485
+
486
+ expect(newTime.hour).toBe(23);
487
+ expect(newTime.minute).toBe(59);
488
+ expect(newTime.second).toBe(59);
489
+ expect(newTime.millisecond).toBe(500);
490
+ });
491
+ });
492
+
493
+ //#endregion
494
+
495
+ //#region isValid
496
+
497
+ describe("isValid", () => {
498
+ it("Valid time returns true", () => {
499
+ const time = new Time(15, 30, 45);
500
+ expect(time.isValid).toBe(true);
501
+ });
502
+
503
+ it("Default constructor is valid time", () => {
504
+ const time = new Time();
505
+ expect(time.isValid).toBe(true);
506
+ });
507
+
508
+ it("Time created with tick is valid", () => {
509
+ const tick = (15 * 60 * 60 + 30 * 60 + 45) * 1000;
510
+ const time = new Time(tick);
511
+ expect(time.isValid).toBe(true);
512
+ });
513
+
514
+ it("Time created with NaN tick has isValid false", () => {
515
+ const time = new Time(NaN);
516
+ expect(time.isValid).toBe(false);
517
+ });
518
+ });
519
+
520
+ //#endregion
521
+
522
+ //#region Formatting
523
+
524
+ describe("toFormatString()", () => {
525
+ it("Formats to HH:mm:ss format", () => {
526
+ const time = new Time(15, 30, 45);
527
+ expect(time.toFormatString("HH:mm:ss")).toBe("15:30:45");
528
+ });
529
+
530
+ it("Formats to HH:mm:ss.fff format", () => {
531
+ const time = new Time(15, 30, 45, 123);
532
+ expect(time.toFormatString("HH:mm:ss.fff")).toBe("15:30:45.123");
533
+ });
534
+
535
+ it("Formats to tt hh:mm:ss format (AM)", () => {
536
+ const time = new Time(9, 30, 45);
537
+ expect(time.toFormatString("tt hh:mm:ss")).toBe("AM 09:30:45");
538
+ });
539
+
540
+ it("Formats to tt hh:mm:ss format (PM)", () => {
541
+ const time = new Time(15, 30, 45);
542
+ expect(time.toFormatString("tt hh:mm:ss")).toBe("PM 03:30:45");
543
+ });
544
+
545
+ it("Formats to H:m:s format (no padding)", () => {
546
+ const time = new Time(9, 5, 3);
547
+ expect(time.toFormatString("H:m:s")).toBe("9:5:3");
548
+ });
549
+ });
550
+
551
+ describe("toString()", () => {
552
+ it("Returns default format HH:mm:ss.fff", () => {
553
+ const time = new Time(15, 30, 45, 123);
554
+ expect(time.toString()).toBe("15:30:45.123");
555
+ });
556
+ });
557
+
558
+ //#endregion
559
+ });
@@ -0,0 +1,74 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { Uuid } from "@simplysm/core-common";
3
+
4
+ describe("Uuid", () => {
5
+ describe("new()", () => {
6
+ it("Generates valid UUID v4 format", () => {
7
+ const uuid = Uuid.new();
8
+ const str = uuid.toString();
9
+
10
+ // UUID v4 format: xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
11
+ expect(str).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/);
12
+ });
13
+
14
+ it("Generates new UUID each time", () => {
15
+ const uuid1 = Uuid.new();
16
+ const uuid2 = Uuid.new();
17
+
18
+ expect(uuid1.toString()).not.toBe(uuid2.toString());
19
+ });
20
+ });
21
+
22
+ describe("fromBytes()", () => {
23
+ it("Creates UUID from 16-byte Uint8Array", () => {
24
+ const bytes = new Uint8Array([
25
+ 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde,
26
+ 0xf0,
27
+ ]);
28
+ const uuid = Uuid.fromBytes(bytes);
29
+
30
+ expect(uuid.toString()).toBe("12345678-9abc-def0-1234-56789abcdef0");
31
+ });
32
+
33
+ it("Throws error for non-16-byte input", () => {
34
+ const bytes = new Uint8Array([0x12, 0x34]);
35
+
36
+ expect(() => Uuid.fromBytes(bytes)).toThrow("UUID byte size must be 16");
37
+ });
38
+ });
39
+
40
+ describe("toBytes()", () => {
41
+ it("Converts UUID to 16-byte Uint8Array", () => {
42
+ const uuid = new Uuid("12345678-9abc-def0-1234-56789abcdef0");
43
+ const bytes = uuid.toBytes();
44
+
45
+ expect(bytes).toBeInstanceOf(Uint8Array);
46
+ expect(bytes.length).toBe(16);
47
+ expect(Array.from(bytes)).toEqual([
48
+ 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde,
49
+ 0xf0,
50
+ ]);
51
+ });
52
+
53
+ it("fromBytes and toBytes are inverse operations", () => {
54
+ const originalBytes = new Uint8Array([
55
+ 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde,
56
+ 0xf0,
57
+ ]);
58
+ const uuid = Uuid.fromBytes(originalBytes);
59
+ const resultBytes = uuid.toBytes();
60
+
61
+ expect(Array.from(resultBytes)).toEqual(Array.from(originalBytes));
62
+ });
63
+ });
64
+
65
+ describe("constructor", () => {
66
+ it("Throws error for invalid UUID format", () => {
67
+ expect(() => new Uuid("invalid-uuid")).toThrow();
68
+ });
69
+
70
+ it("Throws error for mismatched UUID length", () => {
71
+ expect(() => new Uuid("12345678-9abc")).toThrow();
72
+ });
73
+ });
74
+ });