@expressive/mvc 0.53.6 → 0.54.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/dist/esm/context.js +130 -126
- package/dist/esm/control.js +165 -158
- package/dist/esm/index.js +22 -8
- package/dist/esm/instruction/get.js +84 -84
- package/dist/esm/instruction/has.js +73 -70
- package/dist/esm/instruction/ref.js +58 -55
- package/dist/esm/instruction/set.js +77 -80
- package/dist/esm/instruction/use.js +60 -55
- package/dist/esm/model.js +247 -251
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -0
- package/package.json +4 -4
- package/dist/esm/context.test.js +0 -211
- package/dist/esm/control.test.js +0 -183
- package/dist/esm/instruction/get.test.js +0 -645
- package/dist/esm/instruction/has.test.js +0 -354
- package/dist/esm/instruction/ref.test.js +0 -281
- package/dist/esm/instruction/set.test.js +0 -527
- package/dist/esm/instruction/use.test.js +0 -305
- package/dist/esm/mocks.js +0 -20
- package/dist/esm/model.test.js +0 -1541
|
@@ -1,305 +0,0 @@
|
|
|
1
|
-
import { Context } from '../context';
|
|
2
|
-
import { Model } from '../model';
|
|
3
|
-
import { use } from './use';
|
|
4
|
-
describe("instruction", () => {
|
|
5
|
-
it("will run on create", () => {
|
|
6
|
-
class Test extends Model {
|
|
7
|
-
constructor() {
|
|
8
|
-
super(...arguments);
|
|
9
|
-
this.property = use((key) => {
|
|
10
|
-
didRunInstruction(key);
|
|
11
|
-
});
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
const didRunInstruction = jest.fn();
|
|
15
|
-
Test.new();
|
|
16
|
-
expect(didRunInstruction).toBeCalledWith("property");
|
|
17
|
-
});
|
|
18
|
-
describe("symbol", () => {
|
|
19
|
-
it("will use symbol as placeholder", async () => {
|
|
20
|
-
class Test extends Model {
|
|
21
|
-
constructor() {
|
|
22
|
-
super(...arguments);
|
|
23
|
-
this.value = use(() => ({ value: 1 }));
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
const test = new Test();
|
|
27
|
-
if (typeof test.value !== "symbol")
|
|
28
|
-
throw new Error("value is not a symbol");
|
|
29
|
-
else
|
|
30
|
-
expect(test.value.description).toBe("instruction");
|
|
31
|
-
await test.set();
|
|
32
|
-
expect(test.value).toBe(1);
|
|
33
|
-
});
|
|
34
|
-
it("will be deleted prior to instruction", () => {
|
|
35
|
-
class Test extends Model {
|
|
36
|
-
constructor() {
|
|
37
|
-
super(...arguments);
|
|
38
|
-
this.value = use(() => {
|
|
39
|
-
expect("value" in this).toBe(false);
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
Test.new();
|
|
44
|
-
});
|
|
45
|
-
it("will ignore normal symbol", () => {
|
|
46
|
-
class Test extends Model {
|
|
47
|
-
constructor() {
|
|
48
|
-
super(...arguments);
|
|
49
|
-
this.value = Symbol("hello");
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
const test = Test.new();
|
|
53
|
-
expect(typeof test.value).toBe("symbol");
|
|
54
|
-
});
|
|
55
|
-
});
|
|
56
|
-
describe("getter", () => {
|
|
57
|
-
it("will run upon access", () => {
|
|
58
|
-
const mockAccess = jest.fn((_subscriber) => "foobar");
|
|
59
|
-
const mockApply = jest.fn((_key) => mockAccess);
|
|
60
|
-
class Test extends Model {
|
|
61
|
-
constructor() {
|
|
62
|
-
super(...arguments);
|
|
63
|
-
this.property = use(mockApply);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
const instance = Test.new();
|
|
67
|
-
expect(mockApply).toBeCalledWith("property", expect.any(Test), {});
|
|
68
|
-
expect(mockAccess).not.toBeCalled();
|
|
69
|
-
expect(instance.property).toBe("foobar");
|
|
70
|
-
expect(mockAccess).toBeCalledWith(instance);
|
|
71
|
-
});
|
|
72
|
-
it("will pass subscriber if within one", () => {
|
|
73
|
-
const didGetValue = jest.fn();
|
|
74
|
-
class Test extends Model {
|
|
75
|
-
constructor() {
|
|
76
|
-
super(...arguments);
|
|
77
|
-
this.property = use(() => didGetValue);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
const state = Test.new();
|
|
81
|
-
state.get(own => {
|
|
82
|
-
void own.property;
|
|
83
|
-
});
|
|
84
|
-
expect(didGetValue).toBeCalledWith(state);
|
|
85
|
-
});
|
|
86
|
-
it("will not throw suspense if get (required) is false", async () => {
|
|
87
|
-
class Test extends Model {
|
|
88
|
-
constructor() {
|
|
89
|
-
super(...arguments);
|
|
90
|
-
this.value = use(() => ({ get: false }));
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
const test = Test.new('ID');
|
|
94
|
-
const effect = jest.fn((test) => void test.value);
|
|
95
|
-
test.get(effect);
|
|
96
|
-
test.value = "foo";
|
|
97
|
-
await expect(test).toHaveUpdated();
|
|
98
|
-
expect(effect).toBeCalledTimes(2);
|
|
99
|
-
});
|
|
100
|
-
});
|
|
101
|
-
describe("setter", () => {
|
|
102
|
-
it("will reject update if false", () => {
|
|
103
|
-
class Test extends Model {
|
|
104
|
-
constructor() {
|
|
105
|
-
super(...arguments);
|
|
106
|
-
this.value = use(() => ({ set: false }));
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
const test = Test.new('ID');
|
|
110
|
-
const assign = () => test.value = "foo";
|
|
111
|
-
expect(assign).toThrowError(`ID.value is read-only.`);
|
|
112
|
-
});
|
|
113
|
-
it("will prevent update if returns false", async () => {
|
|
114
|
-
const didSetValue = jest.fn((newValue) => {
|
|
115
|
-
if (newValue == "ignore")
|
|
116
|
-
return false;
|
|
117
|
-
});
|
|
118
|
-
class Test extends Model {
|
|
119
|
-
constructor() {
|
|
120
|
-
super(...arguments);
|
|
121
|
-
this.property = use(() => {
|
|
122
|
-
return {
|
|
123
|
-
value: "foobar",
|
|
124
|
-
set: didSetValue
|
|
125
|
-
};
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
const test = Test.new();
|
|
130
|
-
expect(test.property).toBe("foobar");
|
|
131
|
-
test.property = "test";
|
|
132
|
-
expect(didSetValue).toBeCalledWith("test", "foobar");
|
|
133
|
-
expect(test.property).toBe("test");
|
|
134
|
-
await expect(test).toHaveUpdated();
|
|
135
|
-
test.property = "ignore";
|
|
136
|
-
expect(didSetValue).toBeCalledWith("ignore", "test");
|
|
137
|
-
expect(test.property).toBe("test");
|
|
138
|
-
await expect(test).not.toHaveUpdated();
|
|
139
|
-
});
|
|
140
|
-
// duplicate test?
|
|
141
|
-
it("will revert update if returns false", async () => {
|
|
142
|
-
let ignore = false;
|
|
143
|
-
class Test extends Model {
|
|
144
|
-
constructor() {
|
|
145
|
-
super(...arguments);
|
|
146
|
-
this.property = use(() => {
|
|
147
|
-
return {
|
|
148
|
-
value: 0,
|
|
149
|
-
set: (value) => ignore
|
|
150
|
-
? false
|
|
151
|
-
: () => value + 10
|
|
152
|
-
};
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
const instance = Test.new();
|
|
157
|
-
expect(instance.property).toBe(0);
|
|
158
|
-
instance.property = 10;
|
|
159
|
-
expect(instance.property).toBe(20);
|
|
160
|
-
await expect(instance).toHaveUpdated();
|
|
161
|
-
ignore = true;
|
|
162
|
-
instance.property = 0;
|
|
163
|
-
expect(instance.property).toBe(20);
|
|
164
|
-
await expect(instance).not.toHaveUpdated();
|
|
165
|
-
});
|
|
166
|
-
it("will not duplicate explicit update", () => {
|
|
167
|
-
class Test extends Model {
|
|
168
|
-
constructor() {
|
|
169
|
-
super(...arguments);
|
|
170
|
-
this.property = use(() => ({
|
|
171
|
-
value: "foobar",
|
|
172
|
-
set: (value) => () => value + "!"
|
|
173
|
-
}));
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
const test = Test.new();
|
|
177
|
-
const didUpdate = jest.fn();
|
|
178
|
-
test.set(didUpdate);
|
|
179
|
-
expect(test.property).toBe("foobar");
|
|
180
|
-
test.property = "test";
|
|
181
|
-
expect(test.property).toBe("test!");
|
|
182
|
-
expect(didUpdate).toBeCalledTimes(1);
|
|
183
|
-
});
|
|
184
|
-
it("will not update on reassignment", () => {
|
|
185
|
-
class Test extends Model {
|
|
186
|
-
constructor() {
|
|
187
|
-
super(...arguments);
|
|
188
|
-
this.property = use((key) => ({
|
|
189
|
-
value: "foobar",
|
|
190
|
-
set: (value) => {
|
|
191
|
-
return () => value + "!";
|
|
192
|
-
}
|
|
193
|
-
}));
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
const test = Test.new();
|
|
197
|
-
const didUpdate = jest.fn();
|
|
198
|
-
test.set(didUpdate);
|
|
199
|
-
expect(test.property).toBe("foobar");
|
|
200
|
-
test.property = "test";
|
|
201
|
-
expect(test.property).toBe("test!");
|
|
202
|
-
expect(didUpdate).toBeCalledTimes(1);
|
|
203
|
-
});
|
|
204
|
-
});
|
|
205
|
-
});
|
|
206
|
-
describe("model", () => {
|
|
207
|
-
it("will init upon access", () => {
|
|
208
|
-
class Child extends Model {
|
|
209
|
-
constructor() {
|
|
210
|
-
super(...arguments);
|
|
211
|
-
this.value = "foo";
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
class Test extends Model {
|
|
215
|
-
constructor() {
|
|
216
|
-
super(...arguments);
|
|
217
|
-
this.child = use(Child, mockInit);
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
const mockInit = jest.fn();
|
|
221
|
-
Test.new();
|
|
222
|
-
expect(mockInit).toBeCalledTimes(1);
|
|
223
|
-
});
|
|
224
|
-
it("will not create base Model", () => {
|
|
225
|
-
class Test extends Model {
|
|
226
|
-
constructor() {
|
|
227
|
-
super(...arguments);
|
|
228
|
-
// @ts-expect-error
|
|
229
|
-
this.child = use(Model);
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
const attempt = () => Test.new();
|
|
233
|
-
expect(attempt).toThrowError("Cannot create base Model.");
|
|
234
|
-
});
|
|
235
|
-
it('will run callback on every assign', () => {
|
|
236
|
-
class Child extends Model {
|
|
237
|
-
constructor() {
|
|
238
|
-
super(...arguments);
|
|
239
|
-
this.value = "foo";
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
class Parent extends Model {
|
|
243
|
-
constructor() {
|
|
244
|
-
super(...arguments);
|
|
245
|
-
this.child = use(Child, callback);
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
const callback = jest.fn();
|
|
249
|
-
const parent = Parent.new();
|
|
250
|
-
// Initial assignment
|
|
251
|
-
expect(callback).toBeCalledTimes(1);
|
|
252
|
-
parent.child = new Child();
|
|
253
|
-
expect(callback).toBeCalledTimes(2);
|
|
254
|
-
});
|
|
255
|
-
it.todo("will run callback after assign completes");
|
|
256
|
-
it.todo("will run cleanup on reassign");
|
|
257
|
-
it('will only reassign a matching model', () => {
|
|
258
|
-
class Child extends Model {
|
|
259
|
-
}
|
|
260
|
-
class Unrelated extends Model {
|
|
261
|
-
}
|
|
262
|
-
;
|
|
263
|
-
class Parent extends Model {
|
|
264
|
-
constructor() {
|
|
265
|
-
super(...arguments);
|
|
266
|
-
this.child = use(Child);
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
const parent = Parent.new("ID");
|
|
270
|
-
expect(() => {
|
|
271
|
-
parent.child = Unrelated.new("ID");
|
|
272
|
-
}).toThrowError(`ID.child expected Model of type Child but got Unrelated.`);
|
|
273
|
-
expect(() => {
|
|
274
|
-
// @ts-expect-error
|
|
275
|
-
parent.child = undefined;
|
|
276
|
-
}).toThrowError(`ID.child expected Model of type Child but got undefined.`);
|
|
277
|
-
});
|
|
278
|
-
it('will allow undefined', () => {
|
|
279
|
-
class Child extends Model {
|
|
280
|
-
}
|
|
281
|
-
class Parent extends Model {
|
|
282
|
-
constructor() {
|
|
283
|
-
super(...arguments);
|
|
284
|
-
this.child = use(Child, false);
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
const parent = Parent.new("ID");
|
|
288
|
-
expect(parent.child).toBeInstanceOf(Child);
|
|
289
|
-
parent.child = undefined;
|
|
290
|
-
expect(parent.child).toBeUndefined();
|
|
291
|
-
});
|
|
292
|
-
it("will be provided by parent", () => {
|
|
293
|
-
class Child extends Model {
|
|
294
|
-
}
|
|
295
|
-
;
|
|
296
|
-
class Test extends Model {
|
|
297
|
-
constructor() {
|
|
298
|
-
super(...arguments);
|
|
299
|
-
this.child = use(Child);
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
const context = new Context({ Test });
|
|
303
|
-
expect(context.get(Child)).toBeInstanceOf(Child);
|
|
304
|
-
});
|
|
305
|
-
});
|
package/dist/esm/mocks.js
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
export function mockPromise() {
|
|
2
|
-
const methods = {};
|
|
3
|
-
const promise = new Promise((res, rej) => {
|
|
4
|
-
methods.resolve = res;
|
|
5
|
-
methods.reject = rej;
|
|
6
|
-
});
|
|
7
|
-
return Object.assign(promise, methods);
|
|
8
|
-
}
|
|
9
|
-
export function mockWarn() {
|
|
10
|
-
const warn = jest.spyOn(console, "warn");
|
|
11
|
-
afterEach(() => warn.mockReset());
|
|
12
|
-
afterAll(() => warn.mockRestore());
|
|
13
|
-
return warn;
|
|
14
|
-
}
|
|
15
|
-
export function mockError() {
|
|
16
|
-
const error = jest.spyOn(console, "error");
|
|
17
|
-
afterEach(() => error.mockReset());
|
|
18
|
-
afterAll(() => error.mockRestore());
|
|
19
|
-
return error;
|
|
20
|
-
}
|