@buenos-nachos/time-sync 0.1.1 → 0.2.0
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/CHANGELOG.md +16 -0
- package/README.md +3 -0
- package/package.json +6 -1
- package/src/TimeSync.test.ts +294 -73
- package/src/TimeSync.ts +218 -115
- package/src/index.ts +2 -1
- package/src/utilities.ts +1 -0
- package/dist/index.d.mts +0 -275
- package/dist/index.d.mts.map +0 -1
- package/dist/index.d.ts +0 -275
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -409
- package/dist/index.js.map +0 -1
- package/dist/index.mjs +0 -406
- package/dist/index.mjs.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# @buenos-nachos/time-sync
|
|
2
2
|
|
|
3
|
+
## 0.2.0
|
|
4
|
+
|
|
5
|
+
### Breaking Changes
|
|
6
|
+
|
|
7
|
+
- 2f527dd: Changed the default value of `allowDuplicateFunctionCalls` from `false` to `true`
|
|
8
|
+
|
|
9
|
+
### Minor Changes
|
|
10
|
+
|
|
11
|
+
- 5f86fac: Added second parameter to `onUpdate` callback. This value is a value of type `SubscriptionContext` and provides information about the current subscription.
|
|
12
|
+
|
|
13
|
+
## 0.1.2
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- 6189eb2: add README to root directory
|
|
18
|
+
|
|
3
19
|
## 0.1.1
|
|
4
20
|
|
|
5
21
|
### Patch Changes
|
package/README.md
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@buenos-nachos/time-sync",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "Michael Smith <hello@nachos.dev> (https://www.nachos.dev)",
|
|
@@ -8,6 +8,11 @@
|
|
|
8
8
|
"publishConfig": {
|
|
9
9
|
"access": "public"
|
|
10
10
|
},
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "git+https://github.com/buenos-nachos/time-sync.git",
|
|
14
|
+
"directory": "packages/time-sync"
|
|
15
|
+
},
|
|
11
16
|
"keywords": [
|
|
12
17
|
"typescript",
|
|
13
18
|
"utility",
|
package/src/TimeSync.test.ts
CHANGED
|
@@ -4,8 +4,10 @@ import {
|
|
|
4
4
|
type Configuration,
|
|
5
5
|
refreshRates,
|
|
6
6
|
type Snapshot,
|
|
7
|
+
type SubscriptionContext,
|
|
7
8
|
TimeSync,
|
|
8
9
|
} from "./TimeSync";
|
|
10
|
+
import type { Writeable } from "./utilities";
|
|
9
11
|
|
|
10
12
|
const invalidIntervals: readonly number[] = [
|
|
11
13
|
Number.NaN,
|
|
@@ -116,17 +118,26 @@ describe(TimeSync, () => {
|
|
|
116
118
|
const sync = new TimeSync();
|
|
117
119
|
const onUpdate = vi.fn();
|
|
118
120
|
|
|
119
|
-
|
|
121
|
+
const dateBefore = sync.getStateSnapshot().date;
|
|
122
|
+
const unsubscribe = sync.subscribe({
|
|
120
123
|
onUpdate,
|
|
121
124
|
targetRefreshIntervalMs: rate,
|
|
122
125
|
});
|
|
123
126
|
expect(onUpdate).not.toHaveBeenCalled();
|
|
124
127
|
|
|
125
|
-
const dateBefore = sync.getStateSnapshot().date;
|
|
126
128
|
await vi.advanceTimersByTimeAsync(rate);
|
|
127
129
|
const dateAfter = sync.getStateSnapshot().date;
|
|
128
130
|
expect(onUpdate).toHaveBeenCalledTimes(1);
|
|
129
|
-
|
|
131
|
+
|
|
132
|
+
const expectedCtx: SubscriptionContext = {
|
|
133
|
+
unsubscribe,
|
|
134
|
+
isLive: true,
|
|
135
|
+
timeSync: sync,
|
|
136
|
+
intervalLastFulfilledAt: dateAfter,
|
|
137
|
+
registeredAt: dateBefore,
|
|
138
|
+
targetRefreshIntervalMs: rate,
|
|
139
|
+
};
|
|
140
|
+
expect(onUpdate).toHaveBeenCalledWith(dateAfter, expectedCtx);
|
|
130
141
|
|
|
131
142
|
const diff = dateAfter.getTime() - dateBefore.getTime();
|
|
132
143
|
expect(diff).toBe(rate);
|
|
@@ -171,7 +182,7 @@ describe(TimeSync, () => {
|
|
|
171
182
|
it("Always dispatches updates in the order that callbacks were first registered", async ({
|
|
172
183
|
expect,
|
|
173
184
|
}) => {
|
|
174
|
-
const sync = new TimeSync();
|
|
185
|
+
const sync = new TimeSync({ allowDuplicateOnUpdateCalls: false });
|
|
175
186
|
const callOrder: number[] = [];
|
|
176
187
|
|
|
177
188
|
const onUpdate1 = vi.fn(() => {
|
|
@@ -260,7 +271,7 @@ describe(TimeSync, () => {
|
|
|
260
271
|
expect(secondOnUpdate).toHaveBeenCalledTimes(1);
|
|
261
272
|
});
|
|
262
273
|
|
|
263
|
-
it("Calls onUpdate callback
|
|
274
|
+
it("Calls onUpdate callback once for each subscription (even if the same callback was registered multiple times)", async ({
|
|
264
275
|
expect,
|
|
265
276
|
}) => {
|
|
266
277
|
const sync = new TimeSync();
|
|
@@ -274,59 +285,7 @@ describe(TimeSync, () => {
|
|
|
274
285
|
}
|
|
275
286
|
|
|
276
287
|
await vi.advanceTimersByTimeAsync(refreshRates.oneMinute);
|
|
277
|
-
expect(sharedOnUpdate).toHaveBeenCalledTimes(
|
|
278
|
-
});
|
|
279
|
-
|
|
280
|
-
it("Calls onUpdate callback one time total if callback is registered multiple times for different time intervals", async ({
|
|
281
|
-
expect,
|
|
282
|
-
}) => {
|
|
283
|
-
const sync = new TimeSync();
|
|
284
|
-
const sharedOnUpdate = vi.fn();
|
|
285
|
-
|
|
286
|
-
void sync.subscribe({
|
|
287
|
-
onUpdate: sharedOnUpdate,
|
|
288
|
-
targetRefreshIntervalMs: refreshRates.oneHour,
|
|
289
|
-
});
|
|
290
|
-
void sync.subscribe({
|
|
291
|
-
onUpdate: sharedOnUpdate,
|
|
292
|
-
targetRefreshIntervalMs: refreshRates.oneMinute,
|
|
293
|
-
});
|
|
294
|
-
void sync.subscribe({
|
|
295
|
-
onUpdate: sharedOnUpdate,
|
|
296
|
-
targetRefreshIntervalMs: refreshRates.oneSecond,
|
|
297
|
-
});
|
|
298
|
-
|
|
299
|
-
// Testing like this to ensure that for really, really long spans of
|
|
300
|
-
// time, the no duplicated calls logic still holds up
|
|
301
|
-
await vi.advanceTimersByTimeAsync(refreshRates.oneHour);
|
|
302
|
-
const secondsInOneHour = 3600;
|
|
303
|
-
expect(sharedOnUpdate).toHaveBeenCalledTimes(secondsInOneHour);
|
|
304
|
-
});
|
|
305
|
-
|
|
306
|
-
it("Calls onUpdate callback one time total if callback is registered multiple times with a mix of redundant/different intervals", async ({
|
|
307
|
-
expect,
|
|
308
|
-
}) => {
|
|
309
|
-
const sync = new TimeSync();
|
|
310
|
-
const sharedOnUpdate = vi.fn();
|
|
311
|
-
|
|
312
|
-
for (let i = 0; i < 10; i++) {
|
|
313
|
-
void sync.subscribe({
|
|
314
|
-
onUpdate: sharedOnUpdate,
|
|
315
|
-
targetRefreshIntervalMs: refreshRates.oneHour,
|
|
316
|
-
});
|
|
317
|
-
void sync.subscribe({
|
|
318
|
-
onUpdate: sharedOnUpdate,
|
|
319
|
-
targetRefreshIntervalMs: refreshRates.oneMinute,
|
|
320
|
-
});
|
|
321
|
-
void sync.subscribe({
|
|
322
|
-
onUpdate: sharedOnUpdate,
|
|
323
|
-
targetRefreshIntervalMs: refreshRates.oneSecond,
|
|
324
|
-
});
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
await vi.advanceTimersByTimeAsync(refreshRates.oneHour);
|
|
328
|
-
const secondsInOneHour = 3600;
|
|
329
|
-
expect(sharedOnUpdate).toHaveBeenCalledTimes(secondsInOneHour);
|
|
288
|
+
expect(sharedOnUpdate).toHaveBeenCalledTimes(3);
|
|
330
289
|
});
|
|
331
290
|
|
|
332
291
|
it("Lets an external system unsubscribe", async ({ expect }) => {
|
|
@@ -405,7 +364,7 @@ describe(TimeSync, () => {
|
|
|
405
364
|
const snap2 = sync.getStateSnapshot();
|
|
406
365
|
expect(snap2.subscriberCount).toBe(30);
|
|
407
366
|
await vi.advanceTimersByTimeAsync(refreshRates.oneSecond);
|
|
408
|
-
expect(sharedOnUpdate).toHaveBeenCalledTimes(
|
|
367
|
+
expect(sharedOnUpdate).toHaveBeenCalledTimes(30);
|
|
409
368
|
});
|
|
410
369
|
|
|
411
370
|
it("Speeds up interval when new subscriber is added that is faster than all other subscribers", async ({
|
|
@@ -604,6 +563,132 @@ describe(TimeSync, () => {
|
|
|
604
563
|
const dateAfter = sync.getStateSnapshot().date;
|
|
605
564
|
expect(dateAfter).not.toEqual(dateBefore);
|
|
606
565
|
});
|
|
566
|
+
|
|
567
|
+
it("Mutates the isLive context value to be false on unsubscribe", async ({
|
|
568
|
+
expect,
|
|
569
|
+
}) => {
|
|
570
|
+
const sync = new TimeSync();
|
|
571
|
+
|
|
572
|
+
let ejectedContext: SubscriptionContext | undefined;
|
|
573
|
+
const onUpdate = vi.fn((_: unknown, ctx: SubscriptionContext) => {
|
|
574
|
+
ejectedContext = ctx;
|
|
575
|
+
});
|
|
576
|
+
|
|
577
|
+
const unsub = sync.subscribe({
|
|
578
|
+
onUpdate,
|
|
579
|
+
targetRefreshIntervalMs: refreshRates.oneMinute,
|
|
580
|
+
});
|
|
581
|
+
|
|
582
|
+
await vi.advanceTimersByTimeAsync(refreshRates.oneMinute);
|
|
583
|
+
expect(ejectedContext?.isLive).toBe(true);
|
|
584
|
+
|
|
585
|
+
unsub();
|
|
586
|
+
expect(ejectedContext?.isLive).toBe(false);
|
|
587
|
+
});
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
describe("Subscriptions: context values", () => {
|
|
591
|
+
it("Defaults to exposing the TimeSync instance the subscription was registered with", async ({
|
|
592
|
+
expect,
|
|
593
|
+
}) => {
|
|
594
|
+
const sync = new TimeSync();
|
|
595
|
+
|
|
596
|
+
let ejectedSync: TimeSync | null = null;
|
|
597
|
+
const onUpdate = vi.fn((_: unknown, ctx: SubscriptionContext) => {
|
|
598
|
+
ejectedSync = ctx.timeSync;
|
|
599
|
+
});
|
|
600
|
+
|
|
601
|
+
void sync.subscribe({
|
|
602
|
+
onUpdate,
|
|
603
|
+
targetRefreshIntervalMs: refreshRates.oneMinute,
|
|
604
|
+
});
|
|
605
|
+
|
|
606
|
+
await vi.advanceTimersByTimeAsync(refreshRates.oneMinute);
|
|
607
|
+
expect(ejectedSync).toBe(sync);
|
|
608
|
+
});
|
|
609
|
+
|
|
610
|
+
it("Exposes refresh interval used on initialization", async ({
|
|
611
|
+
expect,
|
|
612
|
+
}) => {
|
|
613
|
+
const sync = new TimeSync();
|
|
614
|
+
const interval = refreshRates.oneMinute;
|
|
615
|
+
|
|
616
|
+
let ejectedInterval: number | undefined;
|
|
617
|
+
const onUpdate = vi.fn((_: unknown, ctx: SubscriptionContext) => {
|
|
618
|
+
ejectedInterval = ctx.targetRefreshIntervalMs;
|
|
619
|
+
});
|
|
620
|
+
|
|
621
|
+
void sync.subscribe({
|
|
622
|
+
onUpdate,
|
|
623
|
+
targetRefreshIntervalMs: interval,
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
await vi.advanceTimersByTimeAsync(interval);
|
|
627
|
+
expect(ejectedInterval).toBe(interval);
|
|
628
|
+
});
|
|
629
|
+
|
|
630
|
+
it("Exposes exact same unsubscribe callback as the one returned from the subscribe call", async ({
|
|
631
|
+
expect,
|
|
632
|
+
}) => {
|
|
633
|
+
const sync = new TimeSync();
|
|
634
|
+
|
|
635
|
+
let ejectedUnsub: (() => void) | undefined;
|
|
636
|
+
const onUpdate = vi.fn((_: unknown, ctx: SubscriptionContext) => {
|
|
637
|
+
ejectedUnsub = ctx.unsubscribe;
|
|
638
|
+
});
|
|
639
|
+
|
|
640
|
+
const unsub = sync.subscribe({
|
|
641
|
+
onUpdate,
|
|
642
|
+
targetRefreshIntervalMs: refreshRates.oneMinute,
|
|
643
|
+
});
|
|
644
|
+
|
|
645
|
+
await vi.advanceTimersByTimeAsync(refreshRates.oneMinute);
|
|
646
|
+
expect(ejectedUnsub).toBe(unsub);
|
|
647
|
+
});
|
|
648
|
+
|
|
649
|
+
it("Exposes when the subscription was first set up", async ({ expect }) => {
|
|
650
|
+
const sync = new TimeSync();
|
|
651
|
+
const start = sync.getStateSnapshot().date;
|
|
652
|
+
|
|
653
|
+
let ejectedDate: Date | undefined;
|
|
654
|
+
const onUpdate = vi.fn((_: unknown, ctx: SubscriptionContext) => {
|
|
655
|
+
ejectedDate = ctx.registeredAt;
|
|
656
|
+
});
|
|
657
|
+
|
|
658
|
+
void sync.subscribe({
|
|
659
|
+
onUpdate,
|
|
660
|
+
targetRefreshIntervalMs: refreshRates.oneMinute,
|
|
661
|
+
});
|
|
662
|
+
|
|
663
|
+
await vi.advanceTimersByTimeAsync(refreshRates.oneMinute);
|
|
664
|
+
expect(ejectedDate).toEqual(start);
|
|
665
|
+
});
|
|
666
|
+
|
|
667
|
+
it("Indicates when the last requested interval was fulfilled", async ({
|
|
668
|
+
expect,
|
|
669
|
+
}) => {
|
|
670
|
+
const sync = new TimeSync();
|
|
671
|
+
|
|
672
|
+
const fulfilledValues: (ReadonlyDate | null)[] = [];
|
|
673
|
+
const onUpdate = vi.fn((_: unknown, ctx: SubscriptionContext) => {
|
|
674
|
+
fulfilledValues.push(ctx.intervalLastFulfilledAt);
|
|
675
|
+
});
|
|
676
|
+
|
|
677
|
+
void sync.subscribe({
|
|
678
|
+
onUpdate,
|
|
679
|
+
targetRefreshIntervalMs: refreshRates.oneMinute,
|
|
680
|
+
});
|
|
681
|
+
void sync.subscribe({
|
|
682
|
+
onUpdate: vi.fn(),
|
|
683
|
+
targetRefreshIntervalMs: refreshRates.thirtySeconds,
|
|
684
|
+
});
|
|
685
|
+
|
|
686
|
+
await vi.advanceTimersByTimeAsync(refreshRates.oneMinute);
|
|
687
|
+
const snapAfter = sync.getStateSnapshot().date;
|
|
688
|
+
|
|
689
|
+
expect(onUpdate).toHaveBeenCalledTimes(2);
|
|
690
|
+
expect(fulfilledValues).toEqual([null, snapAfter]);
|
|
691
|
+
});
|
|
607
692
|
});
|
|
608
693
|
|
|
609
694
|
describe("Subscriptions: custom `minimumRefreshIntervalMs` value", () => {
|
|
@@ -646,11 +731,14 @@ describe(TimeSync, () => {
|
|
|
646
731
|
});
|
|
647
732
|
});
|
|
648
733
|
|
|
649
|
-
describe("Subscriptions:
|
|
650
|
-
it("
|
|
651
|
-
|
|
734
|
+
describe("Subscriptions: turning off duplicate function calls", () => {
|
|
735
|
+
it("Calls onUpdate callback once for each subscription (even if the same callback was registered multiple times)", async ({
|
|
736
|
+
expect,
|
|
737
|
+
}) => {
|
|
738
|
+
const sync = new TimeSync({ allowDuplicateOnUpdateCalls: false });
|
|
652
739
|
const sharedOnUpdate = vi.fn();
|
|
653
|
-
|
|
740
|
+
|
|
741
|
+
for (let i = 1; i <= 3; i++) {
|
|
654
742
|
void sync.subscribe({
|
|
655
743
|
onUpdate: sharedOnUpdate,
|
|
656
744
|
targetRefreshIntervalMs: refreshRates.oneMinute,
|
|
@@ -661,21 +749,104 @@ describe(TimeSync, () => {
|
|
|
661
749
|
expect(sharedOnUpdate).toHaveBeenCalledTimes(1);
|
|
662
750
|
});
|
|
663
751
|
|
|
664
|
-
it("
|
|
665
|
-
|
|
666
|
-
|
|
752
|
+
it("Calls onUpdate callback one time total if callback is registered multiple times for different time intervals", async ({
|
|
753
|
+
expect,
|
|
754
|
+
}) => {
|
|
755
|
+
const sync = new TimeSync({ allowDuplicateOnUpdateCalls: false });
|
|
756
|
+
const sharedOnUpdate = vi.fn();
|
|
757
|
+
|
|
758
|
+
void sync.subscribe({
|
|
759
|
+
onUpdate: sharedOnUpdate,
|
|
760
|
+
targetRefreshIntervalMs: refreshRates.oneHour,
|
|
761
|
+
});
|
|
762
|
+
void sync.subscribe({
|
|
763
|
+
onUpdate: sharedOnUpdate,
|
|
764
|
+
targetRefreshIntervalMs: refreshRates.oneMinute,
|
|
765
|
+
});
|
|
766
|
+
void sync.subscribe({
|
|
767
|
+
onUpdate: sharedOnUpdate,
|
|
768
|
+
targetRefreshIntervalMs: refreshRates.oneSecond,
|
|
667
769
|
});
|
|
668
770
|
|
|
771
|
+
// Testing like this to ensure that for really, really long spans of
|
|
772
|
+
// time, the no duplicated calls logic still holds up
|
|
773
|
+
await vi.advanceTimersByTimeAsync(refreshRates.oneHour);
|
|
774
|
+
const secondsInOneHour = 3600;
|
|
775
|
+
expect(sharedOnUpdate).toHaveBeenCalledTimes(secondsInOneHour);
|
|
776
|
+
});
|
|
777
|
+
|
|
778
|
+
it("Calls onUpdate callback one time total if callback is registered multiple times with a mix of redundant/different intervals", async ({
|
|
779
|
+
expect,
|
|
780
|
+
}) => {
|
|
781
|
+
const sync = new TimeSync({ allowDuplicateOnUpdateCalls: false });
|
|
669
782
|
const sharedOnUpdate = vi.fn();
|
|
670
|
-
|
|
783
|
+
|
|
784
|
+
for (let i = 0; i < 10; i++) {
|
|
785
|
+
void sync.subscribe({
|
|
786
|
+
onUpdate: sharedOnUpdate,
|
|
787
|
+
targetRefreshIntervalMs: refreshRates.oneHour,
|
|
788
|
+
});
|
|
671
789
|
void sync.subscribe({
|
|
672
790
|
onUpdate: sharedOnUpdate,
|
|
673
791
|
targetRefreshIntervalMs: refreshRates.oneMinute,
|
|
674
792
|
});
|
|
793
|
+
void sync.subscribe({
|
|
794
|
+
onUpdate: sharedOnUpdate,
|
|
795
|
+
targetRefreshIntervalMs: refreshRates.oneSecond,
|
|
796
|
+
});
|
|
675
797
|
}
|
|
676
798
|
|
|
677
|
-
await vi.advanceTimersByTimeAsync(refreshRates.
|
|
678
|
-
|
|
799
|
+
await vi.advanceTimersByTimeAsync(refreshRates.oneHour);
|
|
800
|
+
const secondsInOneHour = 3600;
|
|
801
|
+
expect(sharedOnUpdate).toHaveBeenCalledTimes(secondsInOneHour);
|
|
802
|
+
});
|
|
803
|
+
|
|
804
|
+
it("Always exposes the context for the subscription that first set up onUpdate", async ({
|
|
805
|
+
expect,
|
|
806
|
+
}) => {
|
|
807
|
+
const sync = new TimeSync();
|
|
808
|
+
const snapBefore = sync.getStateSnapshot().date;
|
|
809
|
+
|
|
810
|
+
let ejectedContext: SubscriptionContext | undefined;
|
|
811
|
+
const onUpdate = vi.fn((_: unknown, ctx: SubscriptionContext) => {
|
|
812
|
+
ejectedContext = ctx;
|
|
813
|
+
});
|
|
814
|
+
|
|
815
|
+
const unsub = sync.subscribe({
|
|
816
|
+
onUpdate,
|
|
817
|
+
targetRefreshIntervalMs: refreshRates.oneHour,
|
|
818
|
+
});
|
|
819
|
+
void sync.subscribe({
|
|
820
|
+
onUpdate,
|
|
821
|
+
targetRefreshIntervalMs: refreshRates.oneMinute,
|
|
822
|
+
});
|
|
823
|
+
void sync.subscribe({
|
|
824
|
+
onUpdate,
|
|
825
|
+
targetRefreshIntervalMs: refreshRates.oneSecond,
|
|
826
|
+
});
|
|
827
|
+
|
|
828
|
+
await vi.advanceTimersByTimeAsync(refreshRates.oneSecond);
|
|
829
|
+
expect(ejectedContext).toEqual<SubscriptionContext>({
|
|
830
|
+
isLive: true,
|
|
831
|
+
intervalLastFulfilledAt: null,
|
|
832
|
+
registeredAt: snapBefore,
|
|
833
|
+
targetRefreshIntervalMs: refreshRates.oneHour,
|
|
834
|
+
timeSync: sync,
|
|
835
|
+
unsubscribe: unsub,
|
|
836
|
+
});
|
|
837
|
+
|
|
838
|
+
const remainingSecondsToOneHour = refreshRates.oneHour - 1000;
|
|
839
|
+
await vi.advanceTimersByTimeAsync(remainingSecondsToOneHour);
|
|
840
|
+
|
|
841
|
+
const snapAfter = sync.getStateSnapshot().date;
|
|
842
|
+
expect(ejectedContext).toEqual<SubscriptionContext>({
|
|
843
|
+
isLive: true,
|
|
844
|
+
intervalLastFulfilledAt: snapAfter,
|
|
845
|
+
registeredAt: snapBefore,
|
|
846
|
+
targetRefreshIntervalMs: refreshRates.oneHour,
|
|
847
|
+
timeSync: sync,
|
|
848
|
+
unsubscribe: unsub,
|
|
849
|
+
});
|
|
679
850
|
});
|
|
680
851
|
});
|
|
681
852
|
|
|
@@ -685,7 +856,11 @@ describe(TimeSync, () => {
|
|
|
685
856
|
}) => {
|
|
686
857
|
const initialDate = setInitialTime("July 4, 1999");
|
|
687
858
|
const minimumRefreshIntervalMs = 5_000_000;
|
|
688
|
-
const sync = new TimeSync({
|
|
859
|
+
const sync = new TimeSync({
|
|
860
|
+
initialDate,
|
|
861
|
+
minimumRefreshIntervalMs,
|
|
862
|
+
allowDuplicateOnUpdateCalls: false,
|
|
863
|
+
});
|
|
689
864
|
|
|
690
865
|
const snap = sync.getStateSnapshot();
|
|
691
866
|
expect(snap).toEqual<Snapshot>({
|
|
@@ -748,9 +923,8 @@ describe(TimeSync, () => {
|
|
|
748
923
|
await vi.advanceTimersByTimeAsync(refreshRates.oneHour);
|
|
749
924
|
|
|
750
925
|
expect(onUpdate).toHaveBeenCalledTimes(1);
|
|
751
|
-
expect(onUpdate).toHaveBeenCalledWith(expect.any(Date));
|
|
752
|
-
|
|
753
926
|
const newSnap = sync.getStateSnapshot();
|
|
927
|
+
expect(onUpdate).toHaveBeenCalledWith(newSnap.date, expect.any(Object));
|
|
754
928
|
expect(newSnap).not.toEqual(initialSnap);
|
|
755
929
|
});
|
|
756
930
|
|
|
@@ -818,7 +992,6 @@ describe(TimeSync, () => {
|
|
|
818
992
|
});
|
|
819
993
|
|
|
820
994
|
it("Prevents mutating properties at runtime", ({ expect }) => {
|
|
821
|
-
type Writeable<T> = { -readonly [Key in keyof T]: T[Key] };
|
|
822
995
|
const sync = new TimeSync();
|
|
823
996
|
|
|
824
997
|
// We have readonly modifiers on the types, but we need to make sure
|
|
@@ -913,6 +1086,28 @@ describe(TimeSync, () => {
|
|
|
913
1086
|
await vi.advanceTimersByTimeAsync(refreshRates.oneMinute);
|
|
914
1087
|
expect(sharedOnUpdate).not.toHaveBeenCalled();
|
|
915
1088
|
});
|
|
1089
|
+
|
|
1090
|
+
it("Mutates the isLive context value to be false", async ({ expect }) => {
|
|
1091
|
+
const sync = new TimeSync();
|
|
1092
|
+
|
|
1093
|
+
let ejectedContext: SubscriptionContext | undefined;
|
|
1094
|
+
const onUpdate = vi.fn((_: unknown, ctx: SubscriptionContext) => {
|
|
1095
|
+
if (ejectedContext === undefined) {
|
|
1096
|
+
ejectedContext = ctx;
|
|
1097
|
+
}
|
|
1098
|
+
});
|
|
1099
|
+
|
|
1100
|
+
void sync.subscribe({
|
|
1101
|
+
onUpdate,
|
|
1102
|
+
targetRefreshIntervalMs: refreshRates.oneMinute,
|
|
1103
|
+
});
|
|
1104
|
+
|
|
1105
|
+
await vi.advanceTimersByTimeAsync(refreshRates.oneMinute);
|
|
1106
|
+
expect(ejectedContext?.isLive).toBe(true);
|
|
1107
|
+
|
|
1108
|
+
sync.clearAll();
|
|
1109
|
+
expect(ejectedContext?.isLive).toBe(false);
|
|
1110
|
+
});
|
|
916
1111
|
});
|
|
917
1112
|
|
|
918
1113
|
/**
|
|
@@ -1067,5 +1262,31 @@ describe(TimeSync, () => {
|
|
|
1067
1262
|
const snap3 = sync.getStateSnapshot().subscriberCount;
|
|
1068
1263
|
expect(snap3).toBe(0);
|
|
1069
1264
|
});
|
|
1265
|
+
|
|
1266
|
+
it("Lets consumers detect whether an update corresponds to the subscription they explicitly set up", async ({
|
|
1267
|
+
expect,
|
|
1268
|
+
}) => {
|
|
1269
|
+
const sync = new TimeSync();
|
|
1270
|
+
const innerOnUpdate = vi.fn();
|
|
1271
|
+
|
|
1272
|
+
void sync.subscribe({
|
|
1273
|
+
targetRefreshIntervalMs: refreshRates.oneMinute,
|
|
1274
|
+
onUpdate: (date, ctx) => {
|
|
1275
|
+
const intervalMatches =
|
|
1276
|
+
date.getTime() === ctx.intervalLastFulfilledAt?.getTime();
|
|
1277
|
+
if (intervalMatches) {
|
|
1278
|
+
innerOnUpdate();
|
|
1279
|
+
}
|
|
1280
|
+
},
|
|
1281
|
+
});
|
|
1282
|
+
|
|
1283
|
+
void sync.subscribe({
|
|
1284
|
+
targetRefreshIntervalMs: refreshRates.thirtySeconds,
|
|
1285
|
+
onUpdate: vi.fn(),
|
|
1286
|
+
});
|
|
1287
|
+
|
|
1288
|
+
await vi.advanceTimersByTimeAsync(refreshRates.oneMinute);
|
|
1289
|
+
expect(innerOnUpdate).toHaveBeenCalledTimes(1);
|
|
1290
|
+
});
|
|
1070
1291
|
});
|
|
1071
1292
|
});
|