@praxisjs/decorators 1.6.1 → 2.0.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 +46 -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/index.d.ts +1 -1
- package/dist/functions/index.d.ts.map +1 -1
- package/dist/functions/index.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 +9 -1
- package/dist/functions/memo.d.ts.map +1 -1
- package/dist/functions/memo.js +44 -30
- 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 +6 -6
- 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 -49
- 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 -31
- 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 +30 -19
- 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/index.ts +1 -1
- package/src/functions/log.ts +44 -46
- package/src/functions/memo.ts +54 -28
- 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 +13 -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 +68 -53
- package/src/properties/function-prop.ts +25 -27
- package/src/properties/history.ts +34 -33
- package/src/properties/index.ts +1 -0
- package/src/properties/persisted.ts +41 -27
- 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 -150
- 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 -168
- 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 -178
- 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 -1226
- 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 -185
- 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 -208
- package/src/__tests__/properties-browser.test.ts +0 -234
- package/src/__tests__/properties.test.ts +0 -1526
- 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,131 +0,0 @@
|
|
|
1
|
-
// @vitest-environment jsdom
|
|
2
|
-
import { describe, it, expect } from "vitest";
|
|
3
|
-
|
|
4
|
-
import { StatefulComponent } from "@praxisjs/core";
|
|
5
|
-
import { Ref, createRef } from "../properties/ref";
|
|
6
|
-
|
|
7
|
-
function fieldCtx(name: string) {
|
|
8
|
-
const initializers: Array<(this: unknown) => void> = [];
|
|
9
|
-
return {
|
|
10
|
-
ctx: {
|
|
11
|
-
name,
|
|
12
|
-
kind: "field" as const,
|
|
13
|
-
addInitializer(fn: (this: unknown) => void) {
|
|
14
|
-
initializers.push(fn);
|
|
15
|
-
},
|
|
16
|
-
} as ClassFieldDecoratorContext,
|
|
17
|
-
run(instance: unknown) {
|
|
18
|
-
initializers.forEach((fn) => { fn.call(instance); });
|
|
19
|
-
},
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
class TestComponent extends StatefulComponent {
|
|
24
|
-
render() { return null; }
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
describe("Ref decorator", () => {
|
|
28
|
-
it("initializes .current to null", () => {
|
|
29
|
-
const { ctx, run } = fieldCtx("elRef");
|
|
30
|
-
Ref()(undefined, ctx);
|
|
31
|
-
|
|
32
|
-
const instance = new TestComponent();
|
|
33
|
-
run(instance);
|
|
34
|
-
|
|
35
|
-
const ref = (instance as unknown as Record<string, unknown>).elRef as { current: unknown };
|
|
36
|
-
expect(ref.current).toBeNull();
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it("is callable and sets .current", () => {
|
|
40
|
-
const { ctx, run } = fieldCtx("elRef");
|
|
41
|
-
Ref<HTMLDivElement>()(undefined, ctx);
|
|
42
|
-
|
|
43
|
-
const instance = new TestComponent();
|
|
44
|
-
run(instance);
|
|
45
|
-
|
|
46
|
-
const ref = (instance as unknown as Record<string, unknown>).elRef as ((el: HTMLDivElement | null) => void) & { current: HTMLDivElement | null };
|
|
47
|
-
const fakeEl = document.createElement("div") as HTMLDivElement;
|
|
48
|
-
ref(fakeEl);
|
|
49
|
-
|
|
50
|
-
expect(ref.current).toBe(fakeEl);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it("calling with null clears .current", () => {
|
|
54
|
-
const { ctx, run } = fieldCtx("elRef");
|
|
55
|
-
Ref<HTMLDivElement>()(undefined, ctx);
|
|
56
|
-
|
|
57
|
-
const instance = new TestComponent();
|
|
58
|
-
run(instance);
|
|
59
|
-
|
|
60
|
-
const ref = (instance as unknown as Record<string, unknown>).elRef as ((el: HTMLDivElement | null) => void) & { current: HTMLDivElement | null };
|
|
61
|
-
const fakeEl = document.createElement("div") as HTMLDivElement;
|
|
62
|
-
ref(fakeEl);
|
|
63
|
-
expect(ref.current).toBe(fakeEl);
|
|
64
|
-
|
|
65
|
-
ref(null);
|
|
66
|
-
expect(ref.current).toBeNull();
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it("returns the same ref object on every access", () => {
|
|
70
|
-
const { ctx, run } = fieldCtx("elRef");
|
|
71
|
-
Ref()(undefined, ctx);
|
|
72
|
-
|
|
73
|
-
const instance = new TestComponent();
|
|
74
|
-
run(instance);
|
|
75
|
-
|
|
76
|
-
const inst = instance as unknown as Record<string, unknown>;
|
|
77
|
-
expect(inst.elRef).toBe(inst.elRef);
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
it("each instance gets its own independent ref", () => {
|
|
81
|
-
const { ctx: ctx1, run: run1 } = fieldCtx("elRef");
|
|
82
|
-
const { ctx: ctx2, run: run2 } = fieldCtx("elRef");
|
|
83
|
-
Ref<HTMLDivElement>()(undefined, ctx1);
|
|
84
|
-
Ref<HTMLDivElement>()(undefined, ctx2);
|
|
85
|
-
|
|
86
|
-
const a = new TestComponent();
|
|
87
|
-
const b = new TestComponent();
|
|
88
|
-
run1(a);
|
|
89
|
-
run2(b);
|
|
90
|
-
|
|
91
|
-
const refA = (a as unknown as Record<string, unknown>).elRef as ((el: HTMLDivElement | null) => void) & { current: HTMLDivElement | null };
|
|
92
|
-
const refB = (b as unknown as Record<string, unknown>).elRef as ((el: HTMLDivElement | null) => void) & { current: HTMLDivElement | null };
|
|
93
|
-
|
|
94
|
-
const el = document.createElement("div") as HTMLDivElement;
|
|
95
|
-
refA(el);
|
|
96
|
-
|
|
97
|
-
expect(refA.current).toBe(el);
|
|
98
|
-
expect(refB.current).toBeNull();
|
|
99
|
-
});
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
describe("createRef", () => {
|
|
103
|
-
it("initializes .current to null", () => {
|
|
104
|
-
const ref = createRef<HTMLDivElement>();
|
|
105
|
-
expect(ref.current).toBeNull();
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
it("is callable and sets .current", () => {
|
|
109
|
-
const ref = createRef<HTMLDivElement>();
|
|
110
|
-
const el = document.createElement("div") as HTMLDivElement;
|
|
111
|
-
ref(el);
|
|
112
|
-
expect(ref.current).toBe(el);
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
it("calling with null clears .current", () => {
|
|
116
|
-
const ref = createRef<HTMLDivElement>();
|
|
117
|
-
const el = document.createElement("div") as HTMLDivElement;
|
|
118
|
-
ref(el);
|
|
119
|
-
ref(null);
|
|
120
|
-
expect(ref.current).toBeNull();
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
it("each call creates an independent ref", () => {
|
|
124
|
-
const a = createRef<HTMLDivElement>();
|
|
125
|
-
const b = createRef<HTMLDivElement>();
|
|
126
|
-
const el = document.createElement("div") as HTMLDivElement;
|
|
127
|
-
a(el);
|
|
128
|
-
expect(a.current).toBe(el);
|
|
129
|
-
expect(b.current).toBeNull();
|
|
130
|
-
});
|
|
131
|
-
});
|
|
@@ -1,246 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
|
-
|
|
3
|
-
import { invalidateResource, _clearCache } from "@praxisjs/core/internal";
|
|
4
|
-
|
|
5
|
-
import { Resource } from "../properties/resource";
|
|
6
|
-
|
|
7
|
-
function makeFieldCtx(name: string) {
|
|
8
|
-
const initializers: Array<(this: unknown) => void> = [];
|
|
9
|
-
return {
|
|
10
|
-
ctx: {
|
|
11
|
-
name,
|
|
12
|
-
kind: "field" as const,
|
|
13
|
-
addInitializer(fn: (this: unknown) => void) { initializers.push(fn); },
|
|
14
|
-
} as ClassFieldDecoratorContext,
|
|
15
|
-
run(instance: unknown) { initializers.forEach((fn) => { fn.call(instance); }); },
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
describe("@Resource", () => {
|
|
20
|
-
it("binds a Resource object to the field", () => {
|
|
21
|
-
const { ctx, run } = makeFieldCtx("users");
|
|
22
|
-
Resource(() => Promise.resolve([]))(undefined, ctx);
|
|
23
|
-
const instance: Record<string, unknown> = {};
|
|
24
|
-
run(instance);
|
|
25
|
-
const r = instance.users as Record<string, unknown>;
|
|
26
|
-
expect(typeof r.data).toBe("function");
|
|
27
|
-
expect(typeof r.pending).toBe("function");
|
|
28
|
-
expect(typeof r.error).toBe("function");
|
|
29
|
-
expect(typeof r.status).toBe("function");
|
|
30
|
-
expect(typeof r.refetch).toBe("function");
|
|
31
|
-
expect(typeof r.cancel).toBe("function");
|
|
32
|
-
expect(typeof r.mutate).toBe("function");
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it("starts in pending state when immediate=true (default)", () => {
|
|
36
|
-
const { ctx, run } = makeFieldCtx("data");
|
|
37
|
-
Resource(() => new Promise(() => {}))(undefined, ctx);
|
|
38
|
-
const instance: Record<string, unknown> = {};
|
|
39
|
-
run(instance);
|
|
40
|
-
const r = instance.data as { pending: () => boolean; status: () => string };
|
|
41
|
-
expect(r.pending()).toBe(true);
|
|
42
|
-
expect(r.status()).toBe("pending");
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it("starts idle when immediate=false", () => {
|
|
46
|
-
const { ctx, run } = makeFieldCtx("data");
|
|
47
|
-
Resource(() => Promise.resolve(42), { immediate: false })(undefined, ctx);
|
|
48
|
-
const instance: Record<string, unknown> = {};
|
|
49
|
-
run(instance);
|
|
50
|
-
const r = instance.data as { status: () => string; pending: () => boolean };
|
|
51
|
-
expect(r.status()).toBe("idle");
|
|
52
|
-
expect(r.pending()).toBe(false);
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it("resolves data asynchronously", async () => {
|
|
56
|
-
const { ctx, run } = makeFieldCtx("items");
|
|
57
|
-
Resource(() => Promise.resolve([1, 2, 3]))(undefined, ctx);
|
|
58
|
-
const instance: Record<string, unknown> = {};
|
|
59
|
-
run(instance);
|
|
60
|
-
const r = instance.items as { data: () => unknown; status: () => string };
|
|
61
|
-
await vi.waitFor(() => r.status() === "success");
|
|
62
|
-
expect(r.data()).toEqual([1, 2, 3]);
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it("sets error state on rejection", async () => {
|
|
66
|
-
const { ctx, run } = makeFieldCtx("items");
|
|
67
|
-
Resource(() => Promise.reject(new Error("fetch failed")))(undefined, ctx);
|
|
68
|
-
const instance: Record<string, unknown> = {};
|
|
69
|
-
run(instance);
|
|
70
|
-
const r = instance.items as { error: () => Error | null; status: () => string };
|
|
71
|
-
await new Promise((res) => setTimeout(res, 0));
|
|
72
|
-
expect(r.status()).toBe("error");
|
|
73
|
-
expect((r.error() as Error).message).toBe("fetch failed");
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
it("each instance gets its own resource", () => {
|
|
77
|
-
const { ctx, run } = makeFieldCtx("r");
|
|
78
|
-
Resource(() => Promise.resolve(0))(undefined, ctx);
|
|
79
|
-
const a: Record<string, unknown> = {};
|
|
80
|
-
const b: Record<string, unknown> = {};
|
|
81
|
-
run(a);
|
|
82
|
-
run(b);
|
|
83
|
-
expect(a.r).not.toBe(b.r);
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it("mutate() updates data immediately", async () => {
|
|
87
|
-
const { ctx, run } = makeFieldCtx("r");
|
|
88
|
-
Resource(() => Promise.resolve(1))(undefined, ctx);
|
|
89
|
-
const instance: Record<string, unknown> = {};
|
|
90
|
-
run(instance);
|
|
91
|
-
const r = instance.r as { mutate: (v: number) => void; data: () => number; status: () => string };
|
|
92
|
-
r.mutate(99);
|
|
93
|
-
expect(r.data()).toBe(99);
|
|
94
|
-
expect(r.status()).toBe("success");
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
it("cancel() stops a pending fetch", async () => {
|
|
98
|
-
const { ctx, run } = makeFieldCtx("r");
|
|
99
|
-
Resource(() => new Promise(() => {}))(undefined, ctx);
|
|
100
|
-
const instance: Record<string, unknown> = {};
|
|
101
|
-
run(instance);
|
|
102
|
-
const r = instance.r as { cancel: () => void; status: () => string };
|
|
103
|
-
r.cancel();
|
|
104
|
-
expect(r.status()).toBe("idle");
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it("uses initialData option", () => {
|
|
108
|
-
const { ctx, run } = makeFieldCtx("r");
|
|
109
|
-
Resource(() => Promise.resolve("new"), { initialData: "default", immediate: false })(undefined, ctx);
|
|
110
|
-
const instance: Record<string, unknown> = {};
|
|
111
|
-
run(instance);
|
|
112
|
-
const r = instance.r as { data: () => string };
|
|
113
|
-
expect(r.data()).toBe("default");
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
it("cancel() called when not pending — no-op, no crash", () => {
|
|
117
|
-
const { ctx, run } = makeFieldCtx("r");
|
|
118
|
-
Resource(() => Promise.resolve(42), { immediate: false })(undefined, ctx);
|
|
119
|
-
const instance: Record<string, unknown> = {};
|
|
120
|
-
run(instance);
|
|
121
|
-
const r = instance.r as { cancel: () => void; status: () => string };
|
|
122
|
-
// Not pending (immediate=false → idle)
|
|
123
|
-
expect(r.status()).toBe("idle");
|
|
124
|
-
expect(() => r.cancel()).not.toThrow();
|
|
125
|
-
expect(r.status()).toBe("idle");
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
it("refetch() called after successful load — re-fetches", async () => {
|
|
129
|
-
let callCount = 0;
|
|
130
|
-
const { ctx, run } = makeFieldCtx("r");
|
|
131
|
-
Resource(() => Promise.resolve(++callCount))(undefined, ctx);
|
|
132
|
-
const instance: Record<string, unknown> = {};
|
|
133
|
-
run(instance);
|
|
134
|
-
const r = instance.r as { refetch: () => void; data: () => number; status: () => string };
|
|
135
|
-
|
|
136
|
-
await vi.waitFor(() => r.status() === "success");
|
|
137
|
-
expect(r.data()).toBe(1);
|
|
138
|
-
|
|
139
|
-
r.refetch();
|
|
140
|
-
await vi.waitFor(() => r.status() === "success" && r.data() === 2);
|
|
141
|
-
expect(callCount).toBe(2);
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
it("passes instance as self when fetcher has arity > 0", async () => {
|
|
145
|
-
const { ctx, run } = makeFieldCtx("r");
|
|
146
|
-
const fetcher = vi.fn((self: Record<string, unknown>) =>
|
|
147
|
-
Promise.resolve(self.value as number),
|
|
148
|
-
);
|
|
149
|
-
Resource(fetcher)(undefined, ctx);
|
|
150
|
-
const inst: Record<string, unknown> = { value: 42 };
|
|
151
|
-
run(inst);
|
|
152
|
-
const r = inst.r as { status: () => string; data: () => number };
|
|
153
|
-
await vi.waitFor(() => r.status() === "success");
|
|
154
|
-
expect(r.data()).toBe(42);
|
|
155
|
-
expect(fetcher).toHaveBeenCalledWith(inst);
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
it("setter is a no-op — assigning to the decorated field has no effect", () => {
|
|
159
|
-
const { ctx, run } = makeFieldCtx("r");
|
|
160
|
-
Resource(() => Promise.resolve(42), { immediate: false })(undefined, ctx);
|
|
161
|
-
const instance: Record<string, unknown> = {};
|
|
162
|
-
run(instance);
|
|
163
|
-
const original = instance.r;
|
|
164
|
-
instance.r = "overwrite-attempt"; // triggers set(): void {} (no-op)
|
|
165
|
-
expect(instance.r).toBe(original);
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
it("onUnmount calls destroy() on the resource", async () => {
|
|
169
|
-
const { ctx, run } = makeFieldCtx("r");
|
|
170
|
-
Resource(() => Promise.resolve(1))(undefined, ctx);
|
|
171
|
-
const inst: Record<string, unknown> = {};
|
|
172
|
-
run(inst);
|
|
173
|
-
const r = inst.r as { destroy: () => void };
|
|
174
|
-
const spy = vi.spyOn(r, "destroy");
|
|
175
|
-
(inst as { onUnmount?: () => void }).onUnmount?.();
|
|
176
|
-
expect(spy).toHaveBeenCalledOnce();
|
|
177
|
-
});
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
describe("@Resource — cache / SWR / invalidation", () => {
|
|
181
|
-
beforeEach(() => { _clearCache(); });
|
|
182
|
-
|
|
183
|
-
function makeFieldCtx(name: string) {
|
|
184
|
-
const initializers: Array<(this: unknown) => void> = [];
|
|
185
|
-
return {
|
|
186
|
-
ctx: {
|
|
187
|
-
name,
|
|
188
|
-
kind: "field" as const,
|
|
189
|
-
addInitializer(fn: (this: unknown) => void) { initializers.push(fn); },
|
|
190
|
-
} as ClassFieldDecoratorContext,
|
|
191
|
-
run(instance: unknown) { initializers.forEach((fn) => { fn.call(instance); }); },
|
|
192
|
-
};
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
it("key: serves stale cached data immediately while refetching (SWR)", async () => {
|
|
196
|
-
const { ctx: ctx1, run: run1 } = makeFieldCtx("r1");
|
|
197
|
-
Resource(() => Promise.resolve("first"), { key: "dec-swr" })(undefined, ctx1);
|
|
198
|
-
const inst1: Record<string, unknown> = {};
|
|
199
|
-
run1(inst1);
|
|
200
|
-
const r1 = inst1.r1 as { status: () => string; data: () => string; destroy: () => void };
|
|
201
|
-
await vi.waitFor(() => r1.status() === "success");
|
|
202
|
-
|
|
203
|
-
// Second decorator instance — should see cached data straight away
|
|
204
|
-
const { ctx: ctx2, run: run2 } = makeFieldCtx("r2");
|
|
205
|
-
Resource(() => Promise.resolve("second"), { key: "dec-swr" })(undefined, ctx2);
|
|
206
|
-
const inst2: Record<string, unknown> = {};
|
|
207
|
-
run2(inst2);
|
|
208
|
-
const r2 = inst2.r2 as { status: () => string; data: () => string; destroy: () => void };
|
|
209
|
-
|
|
210
|
-
expect(r2.data()).toBe("first"); // stale data shown immediately
|
|
211
|
-
await vi.waitFor(() => r2.data() === "second");
|
|
212
|
-
r1.destroy();
|
|
213
|
-
r2.destroy();
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
it("invalidateResource() triggers refetch on decorated field", async () => {
|
|
217
|
-
let call = 0;
|
|
218
|
-
const { ctx, run } = makeFieldCtx("r");
|
|
219
|
-
Resource(() => Promise.resolve(++call), { key: "dec-inv" })(undefined, ctx);
|
|
220
|
-
const inst: Record<string, unknown> = {};
|
|
221
|
-
run(inst);
|
|
222
|
-
const r = inst.r as { status: () => string; data: () => number; destroy: () => void };
|
|
223
|
-
await vi.waitFor(() => r.status() === "success");
|
|
224
|
-
expect(r.data()).toBe(1);
|
|
225
|
-
|
|
226
|
-
invalidateResource("dec-inv");
|
|
227
|
-
await vi.waitFor(() => r.data() === 2);
|
|
228
|
-
r.destroy();
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
it("destroy() via onUnmount unregisters from cache — no refetch after unmount", async () => {
|
|
232
|
-
let call = 0;
|
|
233
|
-
const { ctx, run } = makeFieldCtx("r");
|
|
234
|
-
Resource(() => Promise.resolve(++call), { key: "dec-unreg" })(undefined, ctx);
|
|
235
|
-
const inst: Record<string, unknown> = {};
|
|
236
|
-
run(inst);
|
|
237
|
-
const r = inst.r as { status: () => string; data: () => number; destroy: () => void };
|
|
238
|
-
await vi.waitFor(() => r.status() === "success");
|
|
239
|
-
|
|
240
|
-
r.destroy();
|
|
241
|
-
const before = call;
|
|
242
|
-
invalidateResource("dec-unreg");
|
|
243
|
-
await new Promise((res) => setTimeout(res, 10));
|
|
244
|
-
expect(call).toBe(before);
|
|
245
|
-
});
|
|
246
|
-
});
|
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
// @vitest-environment jsdom
|
|
2
|
-
import { describe, it, expect, vi } from "vitest";
|
|
3
|
-
|
|
4
|
-
import { initSlots, getSlot, Slot } from "../properties/slot";
|
|
5
|
-
import { StatefulComponent } from "@praxisjs/core";
|
|
6
|
-
|
|
7
|
-
// ── initSlots / getSlot ──────────────────────────────────────────────────────
|
|
8
|
-
|
|
9
|
-
describe("initSlots / getSlot", () => {
|
|
10
|
-
it("returns empty default slot when children is null", () => {
|
|
11
|
-
const instance = {};
|
|
12
|
-
initSlots(instance, null);
|
|
13
|
-
expect(getSlot(instance, "default")).toEqual([]);
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
it("returns empty array for unknown slot", () => {
|
|
17
|
-
const instance = {};
|
|
18
|
-
initSlots(instance, null);
|
|
19
|
-
expect(getSlot(instance, "unknown")).toEqual([]);
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it("puts plain children into the default slot", () => {
|
|
23
|
-
const instance = {};
|
|
24
|
-
const child = document.createElement("span");
|
|
25
|
-
initSlots(instance, child);
|
|
26
|
-
expect(getSlot(instance, "default")).toEqual([child]);
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
it("flattens array children into the default slot", () => {
|
|
30
|
-
const instance = {};
|
|
31
|
-
const a = document.createElement("p");
|
|
32
|
-
const b = document.createElement("div");
|
|
33
|
-
initSlots(instance, [a, b]);
|
|
34
|
-
expect(getSlot(instance, "default")).toEqual([a, b]);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it("routes element with slot attribute to named slot", () => {
|
|
38
|
-
const instance = {};
|
|
39
|
-
const el = document.createElement("div");
|
|
40
|
-
el.setAttribute("slot", "header");
|
|
41
|
-
initSlots(instance, el);
|
|
42
|
-
expect(getSlot(instance, "header")).toEqual([el]);
|
|
43
|
-
expect(getSlot(instance, "default")).toEqual([]);
|
|
44
|
-
// slot attribute should be removed
|
|
45
|
-
expect(el.hasAttribute("slot")).toBe(false);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it("handles mixed named and default children", () => {
|
|
49
|
-
const instance = {};
|
|
50
|
-
const named = document.createElement("aside");
|
|
51
|
-
named.setAttribute("slot", "sidebar");
|
|
52
|
-
const plain = document.createElement("main");
|
|
53
|
-
initSlots(instance, [named, plain]);
|
|
54
|
-
expect(getSlot(instance, "sidebar")).toEqual([named]);
|
|
55
|
-
expect(getSlot(instance, "default")).toEqual([plain]);
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it("skips null children in array", () => {
|
|
59
|
-
const instance = {};
|
|
60
|
-
const el = document.createElement("span");
|
|
61
|
-
initSlots(instance, [null, el, undefined]);
|
|
62
|
-
expect(getSlot(instance, "default")).toEqual([el]);
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it("appends to existing named slot when same slot name appears twice", () => {
|
|
66
|
-
const instance = {};
|
|
67
|
-
const a = document.createElement("div");
|
|
68
|
-
a.setAttribute("slot", "header");
|
|
69
|
-
const b = document.createElement("div");
|
|
70
|
-
b.setAttribute("slot", "header");
|
|
71
|
-
initSlots(instance, [a, b]);
|
|
72
|
-
const headerSlot = getSlot(instance, "header");
|
|
73
|
-
expect(headerSlot).toHaveLength(2);
|
|
74
|
-
expect(headerSlot[0]).toBe(a);
|
|
75
|
-
expect(headerSlot[1]).toBe(b);
|
|
76
|
-
});
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
// ── Slot decorator ───────────────────────────────────────────────────────────
|
|
80
|
-
|
|
81
|
-
describe("Slot decorator", () => {
|
|
82
|
-
it("provides the default slot via field decorator", () => {
|
|
83
|
-
class MyComp extends StatefulComponent {
|
|
84
|
-
render() { return null; }
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const initializers: Array<(this: unknown) => void> = [];
|
|
88
|
-
const ctx = {
|
|
89
|
-
name: "default",
|
|
90
|
-
kind: "field" as const,
|
|
91
|
-
addInitializer(fn: (this: unknown) => void) { initializers.push(fn); },
|
|
92
|
-
} as ClassFieldDecoratorContext<StatefulComponent>;
|
|
93
|
-
|
|
94
|
-
Slot()(undefined, ctx);
|
|
95
|
-
|
|
96
|
-
const instance = new MyComp() as unknown as Record<string, unknown>;
|
|
97
|
-
initializers.forEach((fn) => { fn.call(instance); });
|
|
98
|
-
|
|
99
|
-
// No children initialized — slot should be empty
|
|
100
|
-
const children = document.createElement("p");
|
|
101
|
-
initSlots(instance as object, children);
|
|
102
|
-
|
|
103
|
-
expect(Array.isArray((instance as Record<string, unknown>).default)).toBe(true);
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it("uses custom slot name when provided", () => {
|
|
107
|
-
class MyComp extends StatefulComponent {
|
|
108
|
-
render() { return null; }
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
const initializers: Array<(this: unknown) => void> = [];
|
|
112
|
-
const ctx = {
|
|
113
|
-
name: "content",
|
|
114
|
-
kind: "field" as const,
|
|
115
|
-
addInitializer(fn: (this: unknown) => void) { initializers.push(fn); },
|
|
116
|
-
} as ClassFieldDecoratorContext<StatefulComponent>;
|
|
117
|
-
|
|
118
|
-
Slot("header")(undefined, ctx);
|
|
119
|
-
|
|
120
|
-
const instance = new MyComp() as unknown as Record<string, unknown>;
|
|
121
|
-
initializers.forEach((fn) => { fn.call(instance); });
|
|
122
|
-
|
|
123
|
-
const el = document.createElement("nav");
|
|
124
|
-
el.setAttribute("slot", "header");
|
|
125
|
-
initSlots(instance as object, el);
|
|
126
|
-
|
|
127
|
-
const slot = (instance as Record<string, unknown>).content as unknown[];
|
|
128
|
-
expect(slot).toHaveLength(1);
|
|
129
|
-
expect(slot[0]).toBe(el);
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
it("skips warn on direct assignment in production mode", () => {
|
|
133
|
-
class MyComp extends StatefulComponent {
|
|
134
|
-
render() { return null; }
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
const initializers: Array<(this: unknown) => void> = [];
|
|
138
|
-
const ctx = {
|
|
139
|
-
name: "default",
|
|
140
|
-
kind: "field" as const,
|
|
141
|
-
addInitializer(fn: (this: unknown) => void) { initializers.push(fn); },
|
|
142
|
-
} as ClassFieldDecoratorContext<StatefulComponent>;
|
|
143
|
-
|
|
144
|
-
Slot()(undefined, ctx);
|
|
145
|
-
|
|
146
|
-
const instance = new MyComp() as unknown as Record<string, unknown>;
|
|
147
|
-
initializers.forEach((fn) => { fn.call(instance); });
|
|
148
|
-
|
|
149
|
-
const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
|
|
150
|
-
vi.stubEnv("NODE_ENV", "production");
|
|
151
|
-
(instance as Record<string, unknown>).default = "bad";
|
|
152
|
-
expect(warn).not.toHaveBeenCalled();
|
|
153
|
-
vi.unstubAllEnvs();
|
|
154
|
-
warn.mockRestore();
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
it("warns on direct assignment in non-production", () => {
|
|
158
|
-
class MyComp extends StatefulComponent {
|
|
159
|
-
render() { return null; }
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
const initializers: Array<(this: unknown) => void> = [];
|
|
163
|
-
const ctx = {
|
|
164
|
-
name: "default",
|
|
165
|
-
kind: "field" as const,
|
|
166
|
-
addInitializer(fn: (this: unknown) => void) { initializers.push(fn); },
|
|
167
|
-
} as ClassFieldDecoratorContext<StatefulComponent>;
|
|
168
|
-
|
|
169
|
-
Slot()(undefined, ctx);
|
|
170
|
-
|
|
171
|
-
const instance = new MyComp() as unknown as Record<string, unknown>;
|
|
172
|
-
initializers.forEach((fn) => { fn.call(instance); });
|
|
173
|
-
|
|
174
|
-
const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
|
|
175
|
-
(instance as Record<string, unknown>).default = "bad";
|
|
176
|
-
expect(warn).toHaveBeenCalledWith(expect.stringContaining("[Slot]"));
|
|
177
|
-
warn.mockRestore();
|
|
178
|
-
});
|
|
179
|
-
});
|