silentium-components 0.0.78 → 0.0.80
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 +15 -0
- package/dist/silentium-components.cjs +7 -18
- package/dist/silentium-components.cjs.map +1 -1
- package/dist/silentium-components.js +8 -19
- package/dist/silentium-components.js.map +1 -1
- package/dist/silentium-components.min.js +1 -1
- package/dist/silentium-components.min.mjs +1 -1
- package/dist/silentium-components.min.mjs.map +1 -1
- package/dist/silentium-components.mjs +8 -19
- package/dist/silentium-components.mjs.map +1 -1
- package/package.json +2 -2
- package/src/behaviors/BranchLazy.ts +4 -9
- package/src/behaviors/Task.test.ts +61 -0
- package/src/navigation/Router._destroy.test.ts +98 -0
- package/src/navigation/Router.ts +5 -11
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Late, Transport } from "silentium";
|
|
2
|
+
import { Task } from "../behaviors/Task";
|
|
3
|
+
import { afterEach, beforeEach, expect, test, vi } from "vitest";
|
|
4
|
+
|
|
5
|
+
beforeEach(() => {
|
|
6
|
+
vi.useFakeTimers({ shouldAdvanceTime: true });
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
afterEach(() => {
|
|
10
|
+
vi.runOnlyPendingTimers();
|
|
11
|
+
vi.useRealTimers();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test("Task delays emission", () => {
|
|
15
|
+
const $trigger = Late<string>();
|
|
16
|
+
const delayed = Task($trigger, 100);
|
|
17
|
+
const data: string[] = [];
|
|
18
|
+
delayed.event(
|
|
19
|
+
Transport((v) => {
|
|
20
|
+
data.push(v);
|
|
21
|
+
}),
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
$trigger.use("first");
|
|
25
|
+
|
|
26
|
+
// Before delay, no emission
|
|
27
|
+
expect(data).toStrictEqual([]);
|
|
28
|
+
|
|
29
|
+
// Advance time to trigger emission
|
|
30
|
+
vi.advanceTimersByTime(100);
|
|
31
|
+
|
|
32
|
+
expect(data).toStrictEqual(["first"]);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("Task emits only last value when multiple before delay", () => {
|
|
36
|
+
const $trigger = Late<string>();
|
|
37
|
+
const delayed = Task($trigger, 100);
|
|
38
|
+
const data: string[] = [];
|
|
39
|
+
delayed.event(
|
|
40
|
+
Transport((v) => {
|
|
41
|
+
data.push(v);
|
|
42
|
+
}),
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
$trigger.use("first");
|
|
46
|
+
$trigger.use("second");
|
|
47
|
+
$trigger.use("third");
|
|
48
|
+
|
|
49
|
+
// Before delay, no emission
|
|
50
|
+
expect(data).toStrictEqual([]);
|
|
51
|
+
|
|
52
|
+
// Advance time partially
|
|
53
|
+
vi.advanceTimersByTime(50);
|
|
54
|
+
expect(data).toStrictEqual([]);
|
|
55
|
+
|
|
56
|
+
// Advance to full delay
|
|
57
|
+
vi.advanceTimersByTime(50);
|
|
58
|
+
|
|
59
|
+
// Only last value should be emitted
|
|
60
|
+
expect(data).toStrictEqual(["third"]);
|
|
61
|
+
});
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Applied,
|
|
3
|
+
Late,
|
|
4
|
+
Of,
|
|
5
|
+
Shared,
|
|
6
|
+
Transport,
|
|
7
|
+
Event,
|
|
8
|
+
TransportEvent,
|
|
9
|
+
} from "silentium";
|
|
10
|
+
import { Router } from "../navigation/Router";
|
|
11
|
+
import { expect, test, vi } from "vitest";
|
|
12
|
+
|
|
13
|
+
const drop = (dropPart: string) => (value: string) => {
|
|
14
|
+
return value.replace(dropPart, "");
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
test("Router destroys previous route events when switching routes", () => {
|
|
18
|
+
const $url = Late<string>("http://domain.com/");
|
|
19
|
+
const $urlPath = Shared(Applied($url, drop("http://domain.com")));
|
|
20
|
+
const g = vi.fn();
|
|
21
|
+
$urlPath.event(Transport(g));
|
|
22
|
+
|
|
23
|
+
// Create mock destroyable events for routes
|
|
24
|
+
const firstRouteDestroy = vi.fn();
|
|
25
|
+
const secondRouteDestroy = vi.fn();
|
|
26
|
+
const defaultDestroy = vi.fn();
|
|
27
|
+
|
|
28
|
+
const firstRouteEvent = TransportEvent(() =>
|
|
29
|
+
Event<string>((transport) => {
|
|
30
|
+
transport.use("first-route-response");
|
|
31
|
+
return firstRouteDestroy;
|
|
32
|
+
}),
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
const secondRouteEvent = TransportEvent(() =>
|
|
36
|
+
Event<string>((transport) => {
|
|
37
|
+
transport.use("second-route-response");
|
|
38
|
+
return secondRouteDestroy;
|
|
39
|
+
}),
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
const defaultEvent = TransportEvent(() =>
|
|
43
|
+
Event<string>((transport) => {
|
|
44
|
+
transport.use("default-response");
|
|
45
|
+
return defaultDestroy;
|
|
46
|
+
}),
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
const $router = Router(
|
|
50
|
+
$urlPath,
|
|
51
|
+
Of([
|
|
52
|
+
{
|
|
53
|
+
pattern: "^/first$",
|
|
54
|
+
event: firstRouteEvent,
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
pattern: "^/second$",
|
|
58
|
+
event: secondRouteEvent,
|
|
59
|
+
},
|
|
60
|
+
]),
|
|
61
|
+
defaultEvent,
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
const g2 = vi.fn();
|
|
65
|
+
$router.event(Transport(g2));
|
|
66
|
+
|
|
67
|
+
// Initially no route matches, should use default
|
|
68
|
+
expect(g2).toHaveBeenLastCalledWith("default-response");
|
|
69
|
+
expect(defaultDestroy).not.toHaveBeenCalled();
|
|
70
|
+
|
|
71
|
+
// Change to first route
|
|
72
|
+
$url.use("http://domain.com/first");
|
|
73
|
+
|
|
74
|
+
expect(g2).toHaveBeenLastCalledWith("first-route-response");
|
|
75
|
+
expect(defaultDestroy).toHaveBeenCalledTimes(1); // Default should be destroyed
|
|
76
|
+
expect(firstRouteDestroy).not.toHaveBeenCalled();
|
|
77
|
+
|
|
78
|
+
// Change to second route
|
|
79
|
+
$url.use("http://domain.com/second");
|
|
80
|
+
|
|
81
|
+
expect(g2).toHaveBeenLastCalledWith("second-route-response");
|
|
82
|
+
expect(firstRouteDestroy).toHaveBeenCalledTimes(1); // First route should be destroyed
|
|
83
|
+
expect(secondRouteDestroy).not.toHaveBeenCalled();
|
|
84
|
+
|
|
85
|
+
// Change back to no match (default)
|
|
86
|
+
$url.use("http://domain.com/nomatch");
|
|
87
|
+
|
|
88
|
+
expect(g2).toHaveBeenLastCalledWith("default-response");
|
|
89
|
+
expect(secondRouteDestroy).toHaveBeenCalledTimes(1); // Second route should be destroyed
|
|
90
|
+
expect(defaultDestroy).toHaveBeenCalledTimes(1); // Still only once, as it was destroyed before
|
|
91
|
+
|
|
92
|
+
// Change back to first route
|
|
93
|
+
$url.use("http://domain.com/first");
|
|
94
|
+
|
|
95
|
+
expect(g2).toHaveBeenLastCalledWith("first-route-response");
|
|
96
|
+
expect(defaultDestroy).toHaveBeenCalledTimes(2); // Default destroyed again
|
|
97
|
+
expect(firstRouteDestroy).toHaveBeenCalledTimes(1); // Still only once
|
|
98
|
+
});
|
package/src/navigation/Router.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
All,
|
|
3
3
|
DestroyableType,
|
|
4
|
+
DestroyContainer,
|
|
4
5
|
Event,
|
|
5
6
|
EventType,
|
|
6
|
-
isDestroyable,
|
|
7
7
|
Of,
|
|
8
8
|
Transport,
|
|
9
9
|
TransportType,
|
|
@@ -26,15 +26,9 @@ export function Router<T = "string">(
|
|
|
26
26
|
$default: TransportType<void, EventType<T>>,
|
|
27
27
|
): EventType<T> & DestroyableType {
|
|
28
28
|
return Event<T>((transport) => {
|
|
29
|
-
const
|
|
30
|
-
const checkDestroyable = (instance: unknown) => {
|
|
31
|
-
if (isDestroyable(instance)) {
|
|
32
|
-
destroyableList.push(instance);
|
|
33
|
-
}
|
|
34
|
-
};
|
|
29
|
+
const dc = DestroyContainer();
|
|
35
30
|
const destructor = () => {
|
|
36
|
-
|
|
37
|
-
destroyableList.length = 0;
|
|
31
|
+
dc.destroy();
|
|
38
32
|
};
|
|
39
33
|
All($routes, $url).event(
|
|
40
34
|
Transport(([routes, url]) => {
|
|
@@ -54,13 +48,13 @@ export function Router<T = "string">(
|
|
|
54
48
|
|
|
55
49
|
if (index === -1) {
|
|
56
50
|
const instance = $default.use();
|
|
57
|
-
|
|
51
|
+
dc.add(instance);
|
|
58
52
|
instance.event(transport);
|
|
59
53
|
}
|
|
60
54
|
|
|
61
55
|
if (index > -1) {
|
|
62
56
|
const instance = routes[index].event.use();
|
|
63
|
-
|
|
57
|
+
dc.add(instance);
|
|
64
58
|
instance.event(transport);
|
|
65
59
|
}
|
|
66
60
|
}),
|