@praxisjs/decorators 1.7.0 → 2.0.1
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 +41 -0
- package/dist/component/component.d.ts +1 -1
- package/dist/component/component.d.ts.map +1 -1
- package/dist/component/component.js +8 -15
- package/dist/component/component.js.map +1 -1
- package/dist/component/lazy.d.ts +1 -1
- package/dist/component/lazy.d.ts.map +1 -1
- package/dist/component/lazy.js +44 -36
- package/dist/component/lazy.js.map +1 -1
- package/dist/create-class-decorator.d.ts +27 -5
- package/dist/create-class-decorator.d.ts.map +1 -1
- package/dist/create-class-decorator.js +53 -18
- package/dist/create-class-decorator.js.map +1 -1
- package/dist/create-field-decorator.d.ts +42 -3
- package/dist/create-field-decorator.d.ts.map +1 -1
- package/dist/create-field-decorator.js +63 -27
- package/dist/create-field-decorator.js.map +1 -1
- package/dist/create-getter-decorator.d.ts +81 -16
- package/dist/create-getter-decorator.d.ts.map +1 -1
- package/dist/create-getter-decorator.js +127 -15
- package/dist/create-getter-decorator.js.map +1 -1
- package/dist/create-lifecycle-method-decorator.d.ts +21 -3
- package/dist/create-lifecycle-method-decorator.d.ts.map +1 -1
- package/dist/create-lifecycle-method-decorator.js +25 -18
- package/dist/create-lifecycle-method-decorator.js.map +1 -1
- package/dist/create-method-decorator.d.ts +43 -3
- package/dist/create-method-decorator.d.ts.map +1 -1
- package/dist/create-method-decorator.js +49 -8
- package/dist/create-method-decorator.js.map +1 -1
- package/dist/events/command.d.ts +2 -1
- package/dist/events/command.d.ts.map +1 -1
- package/dist/events/command.js +10 -8
- package/dist/events/command.js.map +1 -1
- package/dist/events/emit.d.ts +2 -2
- package/dist/events/emit.d.ts.map +1 -1
- package/dist/events/emit.js +18 -24
- package/dist/events/emit.js.map +1 -1
- package/dist/events/on-command.d.ts +2 -1
- package/dist/events/on-command.d.ts.map +1 -1
- package/dist/events/on-command.js +12 -14
- package/dist/events/on-command.js.map +1 -1
- package/dist/field.d.ts +51 -0
- package/dist/field.d.ts.map +1 -0
- package/dist/field.js +82 -0
- package/dist/field.js.map +1 -0
- package/dist/functions/bind.d.ts +2 -1
- package/dist/functions/bind.d.ts.map +1 -1
- package/dist/functions/bind.js +4 -4
- package/dist/functions/bind.js.map +1 -1
- package/dist/functions/debounce.d.ts +2 -2
- package/dist/functions/debounce.d.ts.map +1 -1
- package/dist/functions/debounce.js +25 -23
- package/dist/functions/debounce.js.map +1 -1
- package/dist/functions/log.d.ts +2 -1
- package/dist/functions/log.d.ts.map +1 -1
- package/dist/functions/log.js +38 -40
- package/dist/functions/log.js.map +1 -1
- package/dist/functions/memo.d.ts +2 -1
- package/dist/functions/memo.d.ts.map +1 -1
- package/dist/functions/memo.js +41 -40
- package/dist/functions/memo.js.map +1 -1
- package/dist/functions/once.d.ts +2 -1
- package/dist/functions/once.d.ts.map +1 -1
- package/dist/functions/once.js +10 -14
- package/dist/functions/once.js.map +1 -1
- package/dist/functions/retry.d.ts +2 -1
- package/dist/functions/retry.d.ts.map +1 -1
- package/dist/functions/retry.js +21 -23
- package/dist/functions/retry.js.map +1 -1
- package/dist/functions/throttle.d.ts +2 -1
- package/dist/functions/throttle.d.ts.map +1 -1
- package/dist/functions/throttle.js +12 -14
- package/dist/functions/throttle.js.map +1 -1
- package/dist/functions/until.d.ts +2 -1
- package/dist/functions/until.d.ts.map +1 -1
- package/dist/functions/until.js +13 -14
- package/dist/functions/until.js.map +1 -1
- package/dist/functions/watch.d.ts +1 -1
- package/dist/functions/watch.d.ts.map +1 -1
- package/dist/functions/watch.js +36 -34
- package/dist/functions/watch.js.map +1 -1
- package/dist/functions/when.d.ts +1 -1
- package/dist/functions/when.d.ts.map +1 -1
- package/dist/functions/when.js +19 -21
- package/dist/functions/when.js.map +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/lifecycle.d.ts +24 -0
- package/dist/lifecycle.d.ts.map +1 -0
- package/dist/lifecycle.js +55 -0
- package/dist/lifecycle.js.map +1 -0
- package/dist/properties/compose.d.ts +3 -2
- package/dist/properties/compose.d.ts.map +1 -1
- package/dist/properties/compose.js +33 -19
- package/dist/properties/compose.js.map +1 -1
- package/dist/properties/computed.d.ts +12 -10
- package/dist/properties/computed.d.ts.map +1 -1
- package/dist/properties/computed.js +26 -21
- package/dist/properties/computed.js.map +1 -1
- package/dist/properties/deep-state.d.ts +2 -1
- package/dist/properties/deep-state.d.ts.map +1 -1
- package/dist/properties/deep-state.js +66 -60
- package/dist/properties/deep-state.js.map +1 -1
- package/dist/properties/function-prop.d.ts +2 -1
- package/dist/properties/function-prop.d.ts.map +1 -1
- package/dist/properties/function-prop.js +20 -22
- package/dist/properties/function-prop.js.map +1 -1
- package/dist/properties/history.d.ts +2 -2
- package/dist/properties/history.d.ts.map +1 -1
- package/dist/properties/history.js +34 -33
- package/dist/properties/history.js.map +1 -1
- package/dist/properties/index.d.ts +1 -0
- package/dist/properties/index.d.ts.map +1 -1
- package/dist/properties/index.js +1 -0
- package/dist/properties/index.js.map +1 -1
- package/dist/properties/persisted.d.ts +2 -1
- package/dist/properties/persisted.d.ts.map +1 -1
- package/dist/properties/persisted.js +24 -18
- package/dist/properties/persisted.js.map +1 -1
- package/dist/properties/prop.d.ts +2 -1
- package/dist/properties/prop.d.ts.map +1 -1
- package/dist/properties/prop.js +26 -22
- package/dist/properties/prop.js.map +1 -1
- package/dist/properties/ref.d.ts +2 -1
- package/dist/properties/ref.d.ts.map +1 -1
- package/dist/properties/ref.js +11 -8
- package/dist/properties/ref.js.map +1 -1
- package/dist/properties/resource.d.ts.map +1 -1
- package/dist/properties/resource.js +21 -16
- package/dist/properties/resource.js.map +1 -1
- package/dist/properties/selector.d.ts +12 -0
- package/dist/properties/selector.d.ts.map +1 -0
- package/dist/properties/selector.js +25 -0
- package/dist/properties/selector.js.map +1 -0
- package/dist/properties/slot.d.ts +2 -1
- package/dist/properties/slot.d.ts.map +1 -1
- package/dist/properties/slot.js +22 -20
- package/dist/properties/slot.js.map +1 -1
- package/dist/properties/state.d.ts.map +1 -1
- package/dist/properties/state.js +27 -16
- package/dist/properties/state.js.map +1 -1
- package/dist/properties/synced.d.ts +2 -1
- package/dist/properties/synced.d.ts.map +1 -1
- package/dist/properties/synced.js +27 -19
- package/dist/properties/synced.js.map +1 -1
- package/dist/shared-method.d.ts +14 -0
- package/dist/shared-method.d.ts.map +1 -0
- package/dist/shared-method.js +87 -0
- package/dist/shared-method.js.map +1 -0
- package/package.json +14 -5
- package/src/component/component.ts +8 -27
- package/src/component/lazy.ts +51 -41
- package/src/create-class-decorator.ts +90 -31
- package/src/create-field-decorator.ts +120 -43
- package/src/create-getter-decorator.ts +263 -66
- package/src/create-lifecycle-method-decorator.ts +50 -28
- package/src/create-method-decorator.ts +119 -13
- package/src/events/command.ts +11 -9
- package/src/events/emit.ts +16 -22
- package/src/events/on-command.ts +17 -18
- package/src/field.ts +116 -0
- package/src/functions/bind.ts +4 -4
- package/src/functions/debounce.ts +25 -23
- package/src/functions/log.ts +44 -46
- package/src/functions/memo.ts +39 -37
- package/src/functions/once.ts +10 -13
- package/src/functions/retry.ts +19 -21
- package/src/functions/throttle.ts +12 -13
- package/src/functions/until.ts +14 -14
- package/src/functions/watch.ts +37 -36
- package/src/functions/when.ts +18 -20
- package/src/index.ts +12 -5
- package/src/lifecycle.ts +74 -0
- package/src/properties/compose.ts +33 -20
- package/src/properties/computed.ts +35 -45
- package/src/properties/deep-state.ts +66 -64
- package/src/properties/function-prop.ts +25 -27
- package/src/properties/history.ts +34 -35
- package/src/properties/index.ts +1 -0
- package/src/properties/persisted.ts +30 -20
- package/src/properties/prop.ts +30 -22
- package/src/properties/ref.ts +12 -10
- package/src/properties/resource.ts +22 -17
- package/src/properties/selector.ts +45 -0
- package/src/properties/slot.ts +28 -22
- package/src/properties/state.ts +32 -16
- package/src/properties/synced.ts +31 -19
- package/src/shared-method.ts +101 -0
- package/dist/__tests__/component.test.d.ts +0 -2
- package/dist/__tests__/component.test.d.ts.map +0 -1
- package/dist/__tests__/component.test.js +0 -147
- package/dist/__tests__/component.test.js.map +0 -1
- package/dist/__tests__/compose.test.d.ts +0 -2
- package/dist/__tests__/compose.test.d.ts.map +0 -1
- package/dist/__tests__/compose.test.js +0 -164
- package/dist/__tests__/compose.test.js.map +0 -1
- package/dist/__tests__/deep-state.test.d.ts +0 -2
- package/dist/__tests__/deep-state.test.d.ts.map +0 -1
- package/dist/__tests__/deep-state.test.js +0 -221
- package/dist/__tests__/deep-state.test.js.map +0 -1
- package/dist/__tests__/events.test.d.ts +0 -2
- package/dist/__tests__/events.test.d.ts.map +0 -1
- package/dist/__tests__/events.test.js +0 -295
- package/dist/__tests__/events.test.js.map +0 -1
- package/dist/__tests__/functions.test.d.ts +0 -2
- package/dist/__tests__/functions.test.d.ts.map +0 -1
- package/dist/__tests__/functions.test.js +0 -618
- package/dist/__tests__/functions.test.js.map +0 -1
- package/dist/__tests__/lazy.test.d.ts +0 -2
- package/dist/__tests__/lazy.test.d.ts.map +0 -1
- package/dist/__tests__/lazy.test.js +0 -227
- package/dist/__tests__/lazy.test.js.map +0 -1
- package/dist/__tests__/memo.test.d.ts +0 -2
- package/dist/__tests__/memo.test.d.ts.map +0 -1
- package/dist/__tests__/memo.test.js +0 -249
- package/dist/__tests__/memo.test.js.map +0 -1
- package/dist/__tests__/properties-browser.test.d.ts +0 -2
- package/dist/__tests__/properties-browser.test.d.ts.map +0 -1
- package/dist/__tests__/properties-browser.test.js +0 -227
- package/dist/__tests__/properties-browser.test.js.map +0 -1
- package/dist/__tests__/properties.test.d.ts +0 -2
- package/dist/__tests__/properties.test.d.ts.map +0 -1
- package/dist/__tests__/properties.test.js +0 -1253
- package/dist/__tests__/properties.test.js.map +0 -1
- package/dist/__tests__/ref.test.d.ts +0 -2
- package/dist/__tests__/ref.test.d.ts.map +0 -1
- package/dist/__tests__/ref.test.js +0 -106
- package/dist/__tests__/ref.test.js.map +0 -1
- package/dist/__tests__/resource.test.d.ts +0 -2
- package/dist/__tests__/resource.test.d.ts.map +0 -1
- package/dist/__tests__/resource.test.js +0 -217
- package/dist/__tests__/resource.test.js.map +0 -1
- package/dist/__tests__/slot.test.d.ts +0 -2
- package/dist/__tests__/slot.test.d.ts.map +0 -1
- package/dist/__tests__/slot.test.js +0 -147
- package/dist/__tests__/slot.test.js.map +0 -1
- package/dist/__tests__/synced.test.d.ts +0 -2
- package/dist/__tests__/synced.test.d.ts.map +0 -1
- package/dist/__tests__/synced.test.js +0 -161
- package/dist/__tests__/synced.test.js.map +0 -1
- package/dist/__tests__/until.test.d.ts +0 -2
- package/dist/__tests__/until.test.d.ts.map +0 -1
- package/dist/__tests__/until.test.js +0 -88
- package/dist/__tests__/until.test.js.map +0 -1
- package/dist/create-accessor-decorator.d.ts +0 -14
- package/dist/create-accessor-decorator.d.ts.map +0 -1
- package/dist/create-accessor-decorator.js +0 -27
- package/dist/create-accessor-decorator.js.map +0 -1
- package/src/__tests__/component.test.ts +0 -167
- package/src/__tests__/compose.test.ts +0 -202
- package/src/__tests__/deep-state.test.ts +0 -265
- package/src/__tests__/events.test.ts +0 -366
- package/src/__tests__/functions.test.ts +0 -803
- package/src/__tests__/lazy.test.ts +0 -296
- package/src/__tests__/memo.test.ts +0 -293
- package/src/__tests__/properties-browser.test.ts +0 -288
- package/src/__tests__/properties.test.ts +0 -1556
- package/src/__tests__/ref.test.ts +0 -131
- package/src/__tests__/resource.test.ts +0 -246
- package/src/__tests__/slot.test.ts +0 -179
- package/src/__tests__/synced.test.ts +0 -193
- package/src/__tests__/until.test.ts +0 -113
- package/src/create-accessor-decorator.ts +0 -45
- package/tsconfig.json +0 -9
|
@@ -1,366 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi } from "vitest";
|
|
2
|
-
|
|
3
|
-
import { StatefulComponent } from "@praxisjs/core";
|
|
4
|
-
import { setComponentDefault } from "@praxisjs/core/internal";
|
|
5
|
-
|
|
6
|
-
import { Command, createCommand } from "../events/command";
|
|
7
|
-
import { Emit } from "../events/emit";
|
|
8
|
-
import { readProp } from "../events/helper";
|
|
9
|
-
import { OnCommand } from "../events/on-command";
|
|
10
|
-
|
|
11
|
-
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
12
|
-
|
|
13
|
-
class TestComponent extends StatefulComponent {
|
|
14
|
-
render() { return null; }
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function methodCtx(name: string) {
|
|
18
|
-
const initializers: Array<(this: unknown) => void> = [];
|
|
19
|
-
return {
|
|
20
|
-
ctx: {
|
|
21
|
-
name,
|
|
22
|
-
kind: "method" as const,
|
|
23
|
-
addInitializer(fn: (this: unknown) => void) { initializers.push(fn); },
|
|
24
|
-
} as ClassMethodDecoratorContext,
|
|
25
|
-
run(instance: unknown) { initializers.forEach((fn) => { fn.call(instance); }); },
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function fieldCtx(name: string) {
|
|
30
|
-
const initializers: Array<(this: unknown) => void> = [];
|
|
31
|
-
return {
|
|
32
|
-
ctx: {
|
|
33
|
-
name,
|
|
34
|
-
kind: "field" as const,
|
|
35
|
-
addInitializer(fn: (this: unknown) => void) { initializers.push(fn); },
|
|
36
|
-
} as ClassFieldDecoratorContext,
|
|
37
|
-
run(instance: unknown) { initializers.forEach((fn) => { fn.call(instance); }); },
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// ── createCommand ─────────────────────────────────────────────────────────────
|
|
42
|
-
|
|
43
|
-
describe("createCommand", () => {
|
|
44
|
-
it("trigger calls all subscribed handlers with the argument", () => {
|
|
45
|
-
const cmd = createCommand<number>();
|
|
46
|
-
const h1 = vi.fn();
|
|
47
|
-
const h2 = vi.fn();
|
|
48
|
-
cmd.subscribe(h1);
|
|
49
|
-
cmd.subscribe(h2);
|
|
50
|
-
cmd.trigger(42);
|
|
51
|
-
expect(h1).toHaveBeenCalledWith(42);
|
|
52
|
-
expect(h2).toHaveBeenCalledWith(42);
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it("trigger does nothing when no handlers are subscribed", () => {
|
|
56
|
-
const cmd = createCommand<string>();
|
|
57
|
-
expect(() => cmd.trigger("hello")).not.toThrow();
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it("subscribe returns an unsubscribe function", () => {
|
|
61
|
-
const cmd = createCommand<void>();
|
|
62
|
-
const handler = vi.fn();
|
|
63
|
-
const unsub = cmd.subscribe(handler);
|
|
64
|
-
unsub();
|
|
65
|
-
cmd.trigger();
|
|
66
|
-
expect(handler).not.toHaveBeenCalled();
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it("void command can be triggered without args", () => {
|
|
70
|
-
const cmd = createCommand();
|
|
71
|
-
const handler = vi.fn();
|
|
72
|
-
cmd.subscribe(handler);
|
|
73
|
-
cmd.trigger(undefined as unknown as void);
|
|
74
|
-
expect(handler).toHaveBeenCalledOnce();
|
|
75
|
-
});
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
// ── Command ──────────────────────────────────────────────────────────────────
|
|
79
|
-
|
|
80
|
-
describe("Command decorator", () => {
|
|
81
|
-
it("exposes a triggerable, subscribable Command on the field", () => {
|
|
82
|
-
const { ctx, run } = fieldCtx("play");
|
|
83
|
-
Command()(undefined, ctx);
|
|
84
|
-
|
|
85
|
-
const instance = new TestComponent();
|
|
86
|
-
run(instance);
|
|
87
|
-
|
|
88
|
-
const cmd = (instance as unknown as Record<string, unknown>).play as Command<void>;
|
|
89
|
-
const handler = vi.fn();
|
|
90
|
-
cmd.subscribe(handler);
|
|
91
|
-
cmd.trigger();
|
|
92
|
-
expect(handler).toHaveBeenCalledOnce();
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it("passes the trigger argument through for typed commands", () => {
|
|
96
|
-
const { ctx, run } = fieldCtx("seek");
|
|
97
|
-
Command<number>()(undefined, ctx);
|
|
98
|
-
|
|
99
|
-
const instance = new TestComponent();
|
|
100
|
-
run(instance);
|
|
101
|
-
|
|
102
|
-
const cmd = (instance as unknown as Record<string, unknown>).seek as Command<number>;
|
|
103
|
-
const handler = vi.fn();
|
|
104
|
-
cmd.subscribe(handler);
|
|
105
|
-
cmd.trigger(12);
|
|
106
|
-
expect(handler).toHaveBeenCalledWith(12);
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
it("returns the same command object on every access", () => {
|
|
110
|
-
const { ctx, run } = fieldCtx("play");
|
|
111
|
-
Command()(undefined, ctx);
|
|
112
|
-
|
|
113
|
-
const instance = new TestComponent();
|
|
114
|
-
run(instance);
|
|
115
|
-
|
|
116
|
-
const inst = instance as unknown as Record<string, unknown>;
|
|
117
|
-
expect(inst.play).toBe(inst.play);
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
it("each instance gets its own independent command", () => {
|
|
121
|
-
const { ctx: ctx1, run: run1 } = fieldCtx("play");
|
|
122
|
-
const { ctx: ctx2, run: run2 } = fieldCtx("play");
|
|
123
|
-
Command()(undefined, ctx1);
|
|
124
|
-
Command()(undefined, ctx2);
|
|
125
|
-
|
|
126
|
-
const a = new TestComponent();
|
|
127
|
-
const b = new TestComponent();
|
|
128
|
-
run1(a);
|
|
129
|
-
run2(b);
|
|
130
|
-
|
|
131
|
-
const cmdA = (a as unknown as Record<string, unknown>).play as Command<void>;
|
|
132
|
-
const cmdB = (b as unknown as Record<string, unknown>).play as Command<void>;
|
|
133
|
-
|
|
134
|
-
const handlerA = vi.fn();
|
|
135
|
-
const handlerB = vi.fn();
|
|
136
|
-
cmdA.subscribe(handlerA);
|
|
137
|
-
cmdB.subscribe(handlerB);
|
|
138
|
-
cmdA.trigger();
|
|
139
|
-
|
|
140
|
-
expect(handlerA).toHaveBeenCalledOnce();
|
|
141
|
-
expect(handlerB).not.toHaveBeenCalled();
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
it("works as a Command prop consumed by OnCommand in a parent/child pair", () => {
|
|
145
|
-
const { ctx: fieldContext, run: runField } = fieldCtx("play");
|
|
146
|
-
Command()(undefined, fieldContext);
|
|
147
|
-
const parent = new TestComponent();
|
|
148
|
-
runField(parent);
|
|
149
|
-
const play = (parent as unknown as Record<string, unknown>).play as Command<void>;
|
|
150
|
-
|
|
151
|
-
const { ctx: methodContext, run: runMethod } = methodCtx("handlePlay");
|
|
152
|
-
const handler = vi.fn();
|
|
153
|
-
OnCommand("play")(handler as never, methodContext as unknown as ClassMethodDecoratorContext<StatefulComponent>);
|
|
154
|
-
const child = new TestComponent({ play });
|
|
155
|
-
runMethod(child);
|
|
156
|
-
|
|
157
|
-
child.onMount?.();
|
|
158
|
-
play.trigger();
|
|
159
|
-
expect(handler).toHaveBeenCalledOnce();
|
|
160
|
-
});
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
// ── readProp ──────────────────────────────────────────────────────────────────
|
|
164
|
-
|
|
165
|
-
describe("readProp", () => {
|
|
166
|
-
it("returns value from raw props when defined", () => {
|
|
167
|
-
const instance = new TestComponent({ myProp: "fromParent" });
|
|
168
|
-
expect(readProp(instance, "myProp")).toBe("fromParent");
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
it("falls back to defaults when raw props do not define the prop", () => {
|
|
172
|
-
const instance = new TestComponent();
|
|
173
|
-
setComponentDefault(instance, "myProp", "default");
|
|
174
|
-
expect(readProp(instance, "myProp")).toBe("default");
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
it("returns undefined when prop is not in either", () => {
|
|
178
|
-
const instance = new TestComponent();
|
|
179
|
-
expect(readProp(instance, "missing")).toBeUndefined();
|
|
180
|
-
});
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
// ── Emit ─────────────────────────────────────────────────────────────────────
|
|
184
|
-
|
|
185
|
-
describe("Emit decorator", () => {
|
|
186
|
-
it("calls the prop callback with the return value of the method", () => {
|
|
187
|
-
const { ctx, run } = methodCtx("handleInput");
|
|
188
|
-
const original = function (this: object, value: string) { return value.toUpperCase(); };
|
|
189
|
-
Emit("onChange")(original as never, ctx as unknown as ClassMethodDecoratorContext<StatefulComponent>);
|
|
190
|
-
|
|
191
|
-
const callback = vi.fn();
|
|
192
|
-
const instance = new TestComponent({ onChange: callback });
|
|
193
|
-
run(instance);
|
|
194
|
-
|
|
195
|
-
const method = (instance as unknown as Record<string, (v: string) => void>).handleInput;
|
|
196
|
-
method.call(instance, "hello");
|
|
197
|
-
expect(callback).toHaveBeenCalledWith("HELLO");
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
it("calls the prop callback with the method args when return value is undefined", () => {
|
|
201
|
-
const { ctx, run } = methodCtx("doSomething");
|
|
202
|
-
const original = function (this: object, a: number, b: number) { return undefined; };
|
|
203
|
-
Emit("onDo")(original as never, ctx as unknown as ClassMethodDecoratorContext<StatefulComponent>);
|
|
204
|
-
|
|
205
|
-
const callback = vi.fn();
|
|
206
|
-
const instance = new TestComponent({ onDo: callback });
|
|
207
|
-
run(instance);
|
|
208
|
-
|
|
209
|
-
const method = (instance as unknown as Record<string, (a: number, b: number) => void>).doSomething;
|
|
210
|
-
method.call(instance, 1, 2);
|
|
211
|
-
expect(callback).toHaveBeenCalledWith(1, 2);
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
it("calls the prop callback with no args when return is undefined and no args", () => {
|
|
215
|
-
const { ctx, run } = methodCtx("fire");
|
|
216
|
-
const original = function (this: object) { return undefined; };
|
|
217
|
-
Emit("onFire")(original as never, ctx as unknown as ClassMethodDecoratorContext<StatefulComponent>);
|
|
218
|
-
|
|
219
|
-
const callback = vi.fn();
|
|
220
|
-
const instance = new TestComponent({ onFire: callback });
|
|
221
|
-
run(instance);
|
|
222
|
-
|
|
223
|
-
const method = (instance as unknown as Record<string, () => void>).fire;
|
|
224
|
-
method.call(instance);
|
|
225
|
-
expect(callback).toHaveBeenCalledOnce();
|
|
226
|
-
expect(callback).toHaveBeenCalledWith();
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
it("does not throw when the prop callback is not a function", () => {
|
|
230
|
-
const { ctx, run } = methodCtx("submit");
|
|
231
|
-
const original = function (this: object) { return "result"; };
|
|
232
|
-
Emit("onSubmit")(original as never, ctx as unknown as ClassMethodDecoratorContext<StatefulComponent>);
|
|
233
|
-
|
|
234
|
-
const instance = new TestComponent(); // no onSubmit prop
|
|
235
|
-
run(instance);
|
|
236
|
-
|
|
237
|
-
const method = (instance as unknown as Record<string, () => void>).submit;
|
|
238
|
-
expect(() => method.call(instance)).not.toThrow();
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
it("still returns the original method's return value", () => {
|
|
242
|
-
const { ctx, run } = methodCtx("compute");
|
|
243
|
-
const original = function (this: object) { return 99; };
|
|
244
|
-
Emit("onResult")(original as never, ctx as unknown as ClassMethodDecoratorContext<StatefulComponent>);
|
|
245
|
-
|
|
246
|
-
const callback = vi.fn();
|
|
247
|
-
const instance = new TestComponent({ onResult: callback });
|
|
248
|
-
run(instance);
|
|
249
|
-
|
|
250
|
-
const method = (instance as unknown as Record<string, () => number>).compute;
|
|
251
|
-
const result = method.call(instance);
|
|
252
|
-
expect(result).toBe(99);
|
|
253
|
-
});
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
// ── OnCommand ─────────────────────────────────────────────────────────────────
|
|
257
|
-
|
|
258
|
-
describe("OnCommand decorator", () => {
|
|
259
|
-
it("subscribes to the command on mount and calls the method when triggered", () => {
|
|
260
|
-
const { ctx, run } = methodCtx("handleCmd");
|
|
261
|
-
const handler = vi.fn();
|
|
262
|
-
OnCommand("myCommand")(handler as never, ctx as unknown as ClassMethodDecoratorContext<StatefulComponent>);
|
|
263
|
-
|
|
264
|
-
const cmd = createCommand<string>();
|
|
265
|
-
const instance = new TestComponent({ myCommand: cmd });
|
|
266
|
-
run(instance);
|
|
267
|
-
|
|
268
|
-
instance.onMount?.();
|
|
269
|
-
cmd.trigger("payload");
|
|
270
|
-
expect(handler).toHaveBeenCalledWith("payload");
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
it("unsubscribes from the command on unmount", () => {
|
|
274
|
-
const { ctx, run } = methodCtx("onMsg");
|
|
275
|
-
const handler = vi.fn();
|
|
276
|
-
OnCommand("msg")(handler as never, ctx as unknown as ClassMethodDecoratorContext<StatefulComponent>);
|
|
277
|
-
|
|
278
|
-
const cmd = createCommand<string>();
|
|
279
|
-
const instance = new TestComponent({ msg: cmd });
|
|
280
|
-
run(instance);
|
|
281
|
-
|
|
282
|
-
instance.onMount?.();
|
|
283
|
-
instance.onUnmount?.();
|
|
284
|
-
cmd.trigger("after-unmount");
|
|
285
|
-
expect(handler).not.toHaveBeenCalled();
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
it("warns when the command prop is not provided", () => {
|
|
289
|
-
const { ctx, run } = methodCtx("onMissing");
|
|
290
|
-
OnCommand("missingProp")(vi.fn() as never, ctx as unknown as ClassMethodDecoratorContext<StatefulComponent>);
|
|
291
|
-
|
|
292
|
-
const instance = new TestComponent(); // no missingProp
|
|
293
|
-
run(instance);
|
|
294
|
-
|
|
295
|
-
const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
|
|
296
|
-
instance.onMount?.();
|
|
297
|
-
expect(warn).toHaveBeenCalledWith(expect.stringContaining("[OnCommand]"));
|
|
298
|
-
warn.mockRestore();
|
|
299
|
-
});
|
|
300
|
-
|
|
301
|
-
it("warns when the command prop is not a valid Command", () => {
|
|
302
|
-
const { ctx, run } = methodCtx("onBadCmd");
|
|
303
|
-
OnCommand("badProp")(vi.fn() as never, ctx as unknown as ClassMethodDecoratorContext<StatefulComponent>);
|
|
304
|
-
|
|
305
|
-
const instance = new TestComponent({ badProp: "not-a-command" });
|
|
306
|
-
run(instance);
|
|
307
|
-
|
|
308
|
-
const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
|
|
309
|
-
instance.onMount?.();
|
|
310
|
-
expect(warn).toHaveBeenCalledWith(expect.stringContaining("[OnCommand]"));
|
|
311
|
-
warn.mockRestore();
|
|
312
|
-
});
|
|
313
|
-
|
|
314
|
-
it("uses 'unknown' as component name when constructor.name is undefined (missing-prop branch)", () => {
|
|
315
|
-
const { ctx, run } = methodCtx("onNoName");
|
|
316
|
-
OnCommand("missingProp")(vi.fn() as never, ctx as unknown as ClassMethodDecoratorContext<StatefulComponent>);
|
|
317
|
-
|
|
318
|
-
// Instance whose constructor.name is undefined to hit ?? "unknown"
|
|
319
|
-
const fakeInstance = Object.assign(new TestComponent(), {
|
|
320
|
-
constructor: { name: undefined },
|
|
321
|
-
});
|
|
322
|
-
run(fakeInstance);
|
|
323
|
-
|
|
324
|
-
const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
|
|
325
|
-
fakeInstance.onMount?.();
|
|
326
|
-
expect(warn).toHaveBeenCalledWith(expect.stringContaining("unknown"));
|
|
327
|
-
warn.mockRestore();
|
|
328
|
-
});
|
|
329
|
-
|
|
330
|
-
it("uses 'unknown' as component name when constructor.name is undefined (invalid-command branch)", () => {
|
|
331
|
-
const { ctx, run } = methodCtx("onNoName2");
|
|
332
|
-
OnCommand("badProp")(vi.fn() as never, ctx as unknown as ClassMethodDecoratorContext<StatefulComponent>);
|
|
333
|
-
|
|
334
|
-
// Instance with a non-command prop and undefined constructor.name
|
|
335
|
-
const fakeInstance = Object.assign(new TestComponent({ badProp: "not-a-command" }), {
|
|
336
|
-
constructor: { name: undefined },
|
|
337
|
-
});
|
|
338
|
-
run(fakeInstance);
|
|
339
|
-
|
|
340
|
-
const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
|
|
341
|
-
fakeInstance.onMount?.();
|
|
342
|
-
expect(warn).toHaveBeenCalledWith(expect.stringContaining("unknown"));
|
|
343
|
-
warn.mockRestore();
|
|
344
|
-
});
|
|
345
|
-
|
|
346
|
-
it("preserves the original onMount and onUnmount implementations", () => {
|
|
347
|
-
const { ctx, run } = methodCtx("onEvt");
|
|
348
|
-
OnCommand("evt")(vi.fn() as never, ctx as unknown as ClassMethodDecoratorContext<StatefulComponent>);
|
|
349
|
-
|
|
350
|
-
const cmd = createCommand<void>();
|
|
351
|
-
const instance = new TestComponent({ evt: cmd });
|
|
352
|
-
run(instance);
|
|
353
|
-
|
|
354
|
-
const mountSpy = vi.fn();
|
|
355
|
-
const unmountSpy = vi.fn();
|
|
356
|
-
const prevMount = instance.onMount?.bind(instance);
|
|
357
|
-
const prevUnmount = instance.onUnmount?.bind(instance);
|
|
358
|
-
instance.onMount = function () { mountSpy(); prevMount?.(); };
|
|
359
|
-
instance.onUnmount = function () { unmountSpy(); prevUnmount?.(); };
|
|
360
|
-
|
|
361
|
-
instance.onMount();
|
|
362
|
-
instance.onUnmount();
|
|
363
|
-
expect(mountSpy).toHaveBeenCalled();
|
|
364
|
-
expect(unmountSpy).toHaveBeenCalled();
|
|
365
|
-
});
|
|
366
|
-
});
|