@quenty/binder 14.40.0 → 14.41.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 +6 -0
- package/package.json +13 -13
- package/src/Shared/Binder.Loading.spec.lua +54 -0
- package/src/Shared/Binder.ObserveBrio.spec.lua +209 -0
- package/src/Shared/Binder.spec.lua +120 -255
- package/src/Shared/BinderGroup.spec.lua +19 -24
- package/src/Shared/BinderTestUtils.lua +163 -0
- package/src/Shared/BinderUtils.spec.lua +94 -103
- package/src/Shared/Collection/BoundChildCollection.spec.lua +36 -45
- package/src/Shared/Promise/promiseBoundClass.spec.lua +21 -27
- package/src/Shared/Trackers/BoundAncestorTracker.spec.lua +36 -44
- package/src/Shared/Trackers/BoundParentTracker.spec.lua +28 -36
|
@@ -1,155 +1,25 @@
|
|
|
1
1
|
--!strict
|
|
2
2
|
--[[
|
|
3
|
-
Comprehensive coverage for Binder.
|
|
4
|
-
|
|
5
|
-
Runtime binders are booted the way production code boots them: registered on a BinderProvider
|
|
6
|
-
that is initialized and started through a ServiceBag, and torn down through serviceBag:Destroy().
|
|
7
|
-
Adornees are parented into a workspace container because CollectionService.GetInstanceAddedSignal
|
|
8
|
-
only fires for instances that live in the DataModel. A bind that happens after :Start() is awaited
|
|
9
|
-
event-driven through `binder:Promise(inst):Yield()`, never a fixed sleep. Tags are global and the
|
|
10
|
-
test place is shared across a batch run, so every test uses a distinct tag and cleans up after
|
|
11
|
-
itself.
|
|
12
|
-
|
|
13
|
-
A handful of tests exercise Binder's standalone lifecycle contract (:new/:Init/:Start guards,
|
|
14
|
-
:Create) and construct the binder directly -- there is no other way to assert, for example, that
|
|
15
|
-
:Start() is idempotent when called twice by hand.
|
|
16
|
-
|
|
17
3
|
@class Binder.spec.lua
|
|
18
4
|
]]
|
|
19
5
|
|
|
20
6
|
local require = require(script.Parent.loader).load(script)
|
|
21
7
|
|
|
22
8
|
local Binder = require("Binder")
|
|
23
|
-
local
|
|
9
|
+
local BinderTestUtils = require("BinderTestUtils")
|
|
24
10
|
local Jest = require("Jest")
|
|
25
|
-
local ServiceBag = require("ServiceBag")
|
|
26
11
|
|
|
27
12
|
local describe = Jest.Globals.describe
|
|
28
13
|
local expect = Jest.Globals.expect
|
|
29
14
|
local it = Jest.Globals.it
|
|
30
15
|
|
|
31
|
-
local
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
-- object graph contains Quenty Signals whose strict __index makes jest's deep-equality traversal
|
|
36
|
-
-- throw. Keeping the class free of them lets toEqual compare instances safely.
|
|
37
|
-
local function makeTrackingClass()
|
|
38
|
-
local Class = {}
|
|
39
|
-
Class.__index = Class
|
|
40
|
-
Class.ClassName = "TrackingClass"
|
|
41
|
-
|
|
42
|
-
function Class.new(inst)
|
|
43
|
-
local self = setmetatable({}, Class)
|
|
44
|
-
self.instance = inst
|
|
45
|
-
self.destroyed = false
|
|
46
|
-
return self
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
function Class:Destroy()
|
|
50
|
-
self.destroyed = true
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
return Class
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
-- Returns once `inst` is no longer bound. CollectionService tag removal is synchronous here, so the
|
|
57
|
-
-- class is usually already gone by the time we check; the guarded wait also covers a deferred case.
|
|
58
|
-
local function awaitUnbound(binder, inst)
|
|
59
|
-
if binder:Get(inst) ~= nil then
|
|
60
|
-
binder:GetClassRemovedSignal():Wait()
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
-- A no-op constructor that still returns a value, so Binder's generic bound type stays inhabited.
|
|
65
|
-
local function noopConstructor()
|
|
66
|
-
return {}
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
local function setup()
|
|
70
|
-
specCounter += 1
|
|
71
|
-
local suffix = specCounter
|
|
72
|
-
|
|
73
|
-
local serviceBag = ServiceBag.new()
|
|
74
|
-
local serviceBagDestroyed = false
|
|
75
|
-
|
|
76
|
-
local container = Instance.new("Folder")
|
|
77
|
-
container.Name = "BinderSpecContainer"
|
|
78
|
-
container.Parent = workspace
|
|
79
|
-
|
|
80
|
-
local instances = {}
|
|
81
|
-
local pendingBinders: { any } = {}
|
|
82
|
-
local extraTagCounter = 0
|
|
83
|
-
local booted = false
|
|
84
|
-
|
|
85
|
-
local function uniqueTag(): string
|
|
86
|
-
extraTagCounter += 1
|
|
87
|
-
return string.format("BinderSpecTag_%d_%d", suffix, extraTagCounter)
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
-- Registers a binder to be booted by boot(); returns the binder so the test can tag/query it.
|
|
91
|
-
-- The constructor is intentionally untyped (any) so per-test generic inference does not leak
|
|
92
|
-
-- into this shared helper.
|
|
93
|
-
local function addBinder(constructor: any, ...): Binder.Binder<any>
|
|
94
|
-
assert(not booted, "Cannot add a binder after boot()")
|
|
95
|
-
local binder = Binder.new(uniqueTag(), constructor, ...)
|
|
96
|
-
table.insert(pendingBinders, binder)
|
|
97
|
-
return binder
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
-- Boots the registered binders through a ServiceBag, exactly as production code does.
|
|
101
|
-
local function boot()
|
|
102
|
-
assert(not booted, "Already booted")
|
|
103
|
-
booted = true
|
|
104
|
-
|
|
105
|
-
local binders = pendingBinders
|
|
106
|
-
local provider = BinderProvider.new(string.format("BinderSpecProvider_%d", suffix), function(self)
|
|
107
|
-
for _, binder in binders do
|
|
108
|
-
self:Add(binder)
|
|
109
|
-
end
|
|
110
|
-
end)
|
|
111
|
-
|
|
112
|
-
serviceBag:GetService(provider)
|
|
113
|
-
serviceBag:Init()
|
|
114
|
-
serviceBag:Start()
|
|
115
|
-
end
|
|
116
|
-
|
|
117
|
-
local function newInstance(parent: Instance?, className: string?): Instance
|
|
118
|
-
local inst = Instance.new(className or "Folder")
|
|
119
|
-
inst.Parent = parent or container
|
|
120
|
-
table.insert(instances, inst)
|
|
121
|
-
return inst
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
local function destroyServiceBag()
|
|
125
|
-
if not serviceBagDestroyed then
|
|
126
|
-
serviceBagDestroyed = true
|
|
127
|
-
serviceBag:Destroy()
|
|
128
|
-
end
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
return {
|
|
132
|
-
container = container,
|
|
133
|
-
uniqueTag = uniqueTag,
|
|
134
|
-
addBinder = addBinder,
|
|
135
|
-
boot = boot,
|
|
136
|
-
newInstance = newInstance,
|
|
137
|
-
destroyServiceBag = destroyServiceBag,
|
|
138
|
-
destroy = function()
|
|
139
|
-
destroyServiceBag()
|
|
140
|
-
for _, inst in instances do
|
|
141
|
-
pcall(function()
|
|
142
|
-
inst:Destroy()
|
|
143
|
-
end)
|
|
144
|
-
end
|
|
145
|
-
container:Destroy()
|
|
146
|
-
end,
|
|
147
|
-
}
|
|
148
|
-
end
|
|
16
|
+
local setup = BinderTestUtils.setup
|
|
17
|
+
local makeTrackingClass = BinderTestUtils.makeTrackingClass
|
|
18
|
+
local awaitUnbound = BinderTestUtils.awaitUnbound
|
|
19
|
+
local noopConstructor = BinderTestUtils.noopConstructor
|
|
149
20
|
|
|
150
21
|
describe("Binder.new()", function()
|
|
151
22
|
it("constructs a binder", function()
|
|
152
|
-
-- Never Init'd/Started, so it holds no resources and needs no teardown.
|
|
153
23
|
local binder = Binder.new("BinderNewSpecTag", noopConstructor)
|
|
154
24
|
expect(Binder.isBinder(binder)).toEqual(true)
|
|
155
25
|
end)
|
|
@@ -173,16 +43,14 @@ describe("Binder.new()", function()
|
|
|
173
43
|
end)
|
|
174
44
|
|
|
175
45
|
it("captures variadic constructor args", function()
|
|
176
|
-
local
|
|
46
|
+
local controller = setup()
|
|
177
47
|
|
|
178
|
-
|
|
179
|
-
-- inject, so this class only ever captures the "a"/"b" passed here.
|
|
180
|
-
local binder = env.addBinder(function(_inst, a, b)
|
|
48
|
+
local binder = controller.addBinder(function(_inst, a, b)
|
|
181
49
|
return { a = a, b = b }
|
|
182
50
|
end, "a", "b")
|
|
183
|
-
|
|
51
|
+
controller.boot()
|
|
184
52
|
|
|
185
|
-
local inst =
|
|
53
|
+
local inst = controller.newInstance()
|
|
186
54
|
binder:Tag(inst)
|
|
187
55
|
|
|
188
56
|
local ok, class = binder:Promise(inst):Yield()
|
|
@@ -190,7 +58,7 @@ describe("Binder.new()", function()
|
|
|
190
58
|
expect(class.a).toEqual("a")
|
|
191
59
|
expect(class.b).toEqual("b")
|
|
192
60
|
|
|
193
|
-
|
|
61
|
+
controller.destroy()
|
|
194
62
|
end)
|
|
195
63
|
end)
|
|
196
64
|
|
|
@@ -226,14 +94,14 @@ end)
|
|
|
226
94
|
|
|
227
95
|
describe("Binder constructor variants", function()
|
|
228
96
|
it("supports a plain function constructor", function()
|
|
229
|
-
local
|
|
97
|
+
local controller = setup()
|
|
230
98
|
|
|
231
|
-
local binder =
|
|
99
|
+
local binder = controller.addBinder(function(inst)
|
|
232
100
|
return { instance = inst, kind = "function" }
|
|
233
101
|
end)
|
|
234
|
-
|
|
102
|
+
controller.boot()
|
|
235
103
|
|
|
236
|
-
local inst =
|
|
104
|
+
local inst = controller.newInstance()
|
|
237
105
|
binder:Tag(inst)
|
|
238
106
|
|
|
239
107
|
local ok, class = binder:Promise(inst):Yield()
|
|
@@ -241,45 +109,45 @@ describe("Binder constructor variants", function()
|
|
|
241
109
|
expect(class.kind).toEqual("function")
|
|
242
110
|
expect(class.instance).toEqual(inst)
|
|
243
111
|
|
|
244
|
-
|
|
112
|
+
controller.destroy()
|
|
245
113
|
end)
|
|
246
114
|
|
|
247
115
|
it("supports a class table with .new", function()
|
|
248
|
-
local
|
|
116
|
+
local controller = setup()
|
|
249
117
|
|
|
250
118
|
local Class = makeTrackingClass()
|
|
251
|
-
local binder =
|
|
252
|
-
|
|
119
|
+
local binder = controller.addBinder(Class)
|
|
120
|
+
controller.boot()
|
|
253
121
|
|
|
254
|
-
local inst =
|
|
122
|
+
local inst = controller.newInstance()
|
|
255
123
|
binder:Tag(inst)
|
|
256
124
|
|
|
257
125
|
local ok, class = binder:Promise(inst):Yield()
|
|
258
126
|
assert(ok, "Never bound")
|
|
259
127
|
expect(class.instance).toEqual(inst)
|
|
260
128
|
|
|
261
|
-
|
|
129
|
+
controller.destroy()
|
|
262
130
|
end)
|
|
263
131
|
|
|
264
132
|
it("supports a provider table with :Create", function()
|
|
265
|
-
local
|
|
133
|
+
local controller = setup()
|
|
266
134
|
|
|
267
135
|
local provider = {}
|
|
268
136
|
function provider.Create(_self, inst)
|
|
269
137
|
return { instance = inst, kind = "provider" }
|
|
270
138
|
end
|
|
271
139
|
|
|
272
|
-
local binder =
|
|
273
|
-
|
|
140
|
+
local binder = controller.addBinder(provider :: any)
|
|
141
|
+
controller.boot()
|
|
274
142
|
|
|
275
|
-
local inst =
|
|
143
|
+
local inst = controller.newInstance()
|
|
276
144
|
binder:Tag(inst)
|
|
277
145
|
|
|
278
146
|
local ok, class = binder:Promise(inst):Yield()
|
|
279
147
|
assert(ok, "Never bound")
|
|
280
148
|
expect(class.kind).toEqual("provider")
|
|
281
149
|
|
|
282
|
-
|
|
150
|
+
controller.destroy()
|
|
283
151
|
end)
|
|
284
152
|
end)
|
|
285
153
|
|
|
@@ -312,88 +180,87 @@ end)
|
|
|
312
180
|
|
|
313
181
|
describe("Binder binding via ServiceBag", function()
|
|
314
182
|
it("binds instances tagged before start", function()
|
|
315
|
-
local
|
|
183
|
+
local controller = setup()
|
|
316
184
|
|
|
317
185
|
local Class = makeTrackingClass()
|
|
318
|
-
local binder =
|
|
186
|
+
local binder = controller.addBinder(Class)
|
|
319
187
|
|
|
320
|
-
local inst =
|
|
321
|
-
binder:Tag(inst)
|
|
188
|
+
local inst = controller.newInstance()
|
|
189
|
+
binder:Tag(inst)
|
|
322
190
|
|
|
323
|
-
|
|
191
|
+
controller.boot()
|
|
324
192
|
|
|
325
|
-
-- Pre-tagged instances bind synchronously as the binder starts.
|
|
326
193
|
expect(binder:Get(inst)).never.toBeNil()
|
|
327
194
|
|
|
328
|
-
|
|
195
|
+
controller.destroy()
|
|
329
196
|
end)
|
|
330
197
|
|
|
331
198
|
it("binds instances tagged after start", function()
|
|
332
|
-
local
|
|
199
|
+
local controller = setup()
|
|
333
200
|
|
|
334
|
-
local binder =
|
|
335
|
-
|
|
201
|
+
local binder = controller.addBinder(makeTrackingClass())
|
|
202
|
+
controller.boot()
|
|
336
203
|
|
|
337
|
-
local inst =
|
|
204
|
+
local inst = controller.newInstance()
|
|
338
205
|
binder:Tag(inst)
|
|
339
206
|
|
|
340
207
|
local ok = binder:Promise(inst):Yield()
|
|
341
208
|
expect(ok).toEqual(true)
|
|
342
209
|
|
|
343
|
-
|
|
210
|
+
controller.destroy()
|
|
344
211
|
end)
|
|
345
212
|
end)
|
|
346
213
|
|
|
347
214
|
describe("Binder:Get()", function()
|
|
348
215
|
it("returns nil for an unbound instance", function()
|
|
349
|
-
local
|
|
216
|
+
local controller = setup()
|
|
350
217
|
|
|
351
|
-
local binder =
|
|
352
|
-
|
|
218
|
+
local binder = controller.addBinder(function() end)
|
|
219
|
+
controller.boot()
|
|
353
220
|
|
|
354
|
-
expect(binder:Get(
|
|
221
|
+
expect(binder:Get(controller.newInstance())).toBeNil()
|
|
355
222
|
|
|
356
|
-
|
|
223
|
+
controller.destroy()
|
|
357
224
|
end)
|
|
358
225
|
|
|
359
226
|
it("throws when passed a non-instance", function()
|
|
360
|
-
local
|
|
227
|
+
local controller = setup()
|
|
361
228
|
|
|
362
|
-
local binder =
|
|
363
|
-
|
|
229
|
+
local binder = controller.addBinder(function() end)
|
|
230
|
+
controller.boot()
|
|
364
231
|
|
|
365
232
|
expect(function()
|
|
366
233
|
binder:Get(5 :: any)
|
|
367
234
|
end).toThrow()
|
|
368
235
|
|
|
369
|
-
|
|
236
|
+
controller.destroy()
|
|
370
237
|
end)
|
|
371
238
|
end)
|
|
372
239
|
|
|
373
240
|
describe("Binder tagging", function()
|
|
374
241
|
it("Tag/HasTag/Untag manage the collection service tag", function()
|
|
375
|
-
local
|
|
242
|
+
local controller = setup()
|
|
376
243
|
|
|
377
|
-
local binder =
|
|
378
|
-
|
|
244
|
+
local binder = controller.addBinder(function() end)
|
|
245
|
+
controller.boot()
|
|
379
246
|
|
|
380
|
-
local inst =
|
|
247
|
+
local inst = controller.newInstance()
|
|
381
248
|
expect(binder:HasTag(inst)).toEqual(false)
|
|
382
249
|
binder:Tag(inst)
|
|
383
250
|
expect(binder:HasTag(inst)).toEqual(true)
|
|
384
251
|
binder:Untag(inst)
|
|
385
252
|
expect(binder:HasTag(inst)).toEqual(false)
|
|
386
253
|
|
|
387
|
-
|
|
254
|
+
controller.destroy()
|
|
388
255
|
end)
|
|
389
256
|
|
|
390
257
|
it("Bind tags and Unbind untags on the server", function()
|
|
391
|
-
local
|
|
258
|
+
local controller = setup()
|
|
392
259
|
|
|
393
|
-
local binder =
|
|
394
|
-
|
|
260
|
+
local binder = controller.addBinder(makeTrackingClass())
|
|
261
|
+
controller.boot()
|
|
395
262
|
|
|
396
|
-
local inst =
|
|
263
|
+
local inst = controller.newInstance()
|
|
397
264
|
binder:Bind(inst)
|
|
398
265
|
expect(binder:HasTag(inst)).toEqual(true)
|
|
399
266
|
|
|
@@ -403,7 +270,7 @@ describe("Binder tagging", function()
|
|
|
403
270
|
binder:Unbind(inst)
|
|
404
271
|
expect(binder:HasTag(inst)).toEqual(false)
|
|
405
272
|
|
|
406
|
-
|
|
273
|
+
controller.destroy()
|
|
407
274
|
end)
|
|
408
275
|
end)
|
|
409
276
|
|
|
@@ -439,13 +306,13 @@ end)
|
|
|
439
306
|
|
|
440
307
|
describe("Binder:GetAll() / GetAllSet()", function()
|
|
441
308
|
it("tracks every bound class", function()
|
|
442
|
-
local
|
|
309
|
+
local controller = setup()
|
|
443
310
|
|
|
444
|
-
local binder =
|
|
445
|
-
|
|
311
|
+
local binder = controller.addBinder(makeTrackingClass())
|
|
312
|
+
controller.boot()
|
|
446
313
|
|
|
447
|
-
local instA =
|
|
448
|
-
local instB =
|
|
314
|
+
local instA = controller.newInstance()
|
|
315
|
+
local instB = controller.newInstance()
|
|
449
316
|
binder:Tag(instA)
|
|
450
317
|
binder:Tag(instB)
|
|
451
318
|
|
|
@@ -458,29 +325,29 @@ describe("Binder:GetAll() / GetAllSet()", function()
|
|
|
458
325
|
expect(set[binder:Get(instA)]).toEqual(true)
|
|
459
326
|
expect(set[binder:Get(instB)]).toEqual(true)
|
|
460
327
|
|
|
461
|
-
|
|
328
|
+
controller.destroy()
|
|
462
329
|
end)
|
|
463
330
|
end)
|
|
464
331
|
|
|
465
332
|
describe("Binder signals", function()
|
|
466
333
|
it("memoizes the added/removing/removed signals", function()
|
|
467
|
-
local
|
|
334
|
+
local controller = setup()
|
|
468
335
|
|
|
469
|
-
local binder =
|
|
470
|
-
|
|
336
|
+
local binder = controller.addBinder(function() end)
|
|
337
|
+
controller.boot()
|
|
471
338
|
|
|
472
339
|
expect(binder:GetClassAddedSignal()).toEqual(binder:GetClassAddedSignal())
|
|
473
340
|
expect(binder:GetClassRemovingSignal()).toEqual(binder:GetClassRemovingSignal())
|
|
474
341
|
expect(binder:GetClassRemovedSignal()).toEqual(binder:GetClassRemovedSignal())
|
|
475
342
|
|
|
476
|
-
|
|
343
|
+
controller.destroy()
|
|
477
344
|
end)
|
|
478
345
|
|
|
479
346
|
it("fires the added signal with the class and instance", function()
|
|
480
|
-
local
|
|
347
|
+
local controller = setup()
|
|
481
348
|
|
|
482
|
-
local binder =
|
|
483
|
-
|
|
349
|
+
local binder = controller.addBinder(makeTrackingClass())
|
|
350
|
+
controller.boot()
|
|
484
351
|
|
|
485
352
|
local firedClass, firedInst
|
|
486
353
|
binder:GetClassAddedSignal():Connect(function(class, inst)
|
|
@@ -488,21 +355,21 @@ describe("Binder signals", function()
|
|
|
488
355
|
firedInst = inst
|
|
489
356
|
end)
|
|
490
357
|
|
|
491
|
-
local inst =
|
|
358
|
+
local inst = controller.newInstance()
|
|
492
359
|
binder:Tag(inst)
|
|
493
360
|
assert((binder:Promise(inst):Yield()), "Never bound")
|
|
494
361
|
|
|
495
362
|
expect(firedInst).toEqual(inst)
|
|
496
363
|
expect(firedClass).toEqual(binder:Get(inst))
|
|
497
364
|
|
|
498
|
-
|
|
365
|
+
controller.destroy()
|
|
499
366
|
end)
|
|
500
367
|
|
|
501
368
|
it("fires removing then removed on unbind", function()
|
|
502
|
-
local
|
|
369
|
+
local controller = setup()
|
|
503
370
|
|
|
504
|
-
local binder =
|
|
505
|
-
|
|
371
|
+
local binder = controller.addBinder(makeTrackingClass())
|
|
372
|
+
controller.boot()
|
|
506
373
|
|
|
507
374
|
local order = {}
|
|
508
375
|
binder:GetClassRemovingSignal():Connect(function()
|
|
@@ -512,7 +379,7 @@ describe("Binder signals", function()
|
|
|
512
379
|
table.insert(order, "removed")
|
|
513
380
|
end)
|
|
514
381
|
|
|
515
|
-
local inst =
|
|
382
|
+
local inst = controller.newInstance()
|
|
516
383
|
binder:Tag(inst)
|
|
517
384
|
assert((binder:Promise(inst):Yield()), "Never bound")
|
|
518
385
|
|
|
@@ -522,16 +389,16 @@ describe("Binder signals", function()
|
|
|
522
389
|
expect(order[1]).toEqual("removing")
|
|
523
390
|
expect(order[2]).toEqual("removed")
|
|
524
391
|
|
|
525
|
-
|
|
392
|
+
controller.destroy()
|
|
526
393
|
end)
|
|
527
394
|
|
|
528
395
|
it("destroys the bound class when it is removed", function()
|
|
529
|
-
local
|
|
396
|
+
local controller = setup()
|
|
530
397
|
|
|
531
|
-
local binder =
|
|
532
|
-
|
|
398
|
+
local binder = controller.addBinder(makeTrackingClass())
|
|
399
|
+
controller.boot()
|
|
533
400
|
|
|
534
|
-
local inst =
|
|
401
|
+
local inst = controller.newInstance()
|
|
535
402
|
binder:Tag(inst)
|
|
536
403
|
local ok, class = binder:Promise(inst):Yield()
|
|
537
404
|
assert(ok, "Never bound")
|
|
@@ -541,18 +408,18 @@ describe("Binder signals", function()
|
|
|
541
408
|
|
|
542
409
|
expect(class.destroyed).toEqual(true)
|
|
543
410
|
|
|
544
|
-
|
|
411
|
+
controller.destroy()
|
|
545
412
|
end)
|
|
546
413
|
end)
|
|
547
414
|
|
|
548
415
|
describe("Binder:ObserveInstance()", function()
|
|
549
416
|
it("fires with the class on bind and nil on unbind", function()
|
|
550
|
-
local
|
|
417
|
+
local controller = setup()
|
|
551
418
|
|
|
552
|
-
local binder =
|
|
553
|
-
|
|
419
|
+
local binder = controller.addBinder(makeTrackingClass())
|
|
420
|
+
controller.boot()
|
|
554
421
|
|
|
555
|
-
local inst =
|
|
422
|
+
local inst = controller.newInstance()
|
|
556
423
|
|
|
557
424
|
-- Wrap each emission so a nil value is still recorded (table.insert(t, nil) is a no-op).
|
|
558
425
|
local emissions = {}
|
|
@@ -573,16 +440,16 @@ describe("Binder:ObserveInstance()", function()
|
|
|
573
440
|
|
|
574
441
|
cleanup()
|
|
575
442
|
|
|
576
|
-
|
|
443
|
+
controller.destroy()
|
|
577
444
|
end)
|
|
578
445
|
|
|
579
446
|
it("returns a cleanup that stops future callbacks", function()
|
|
580
|
-
local
|
|
447
|
+
local controller = setup()
|
|
581
448
|
|
|
582
|
-
local binder =
|
|
583
|
-
|
|
449
|
+
local binder = controller.addBinder(makeTrackingClass())
|
|
450
|
+
controller.boot()
|
|
584
451
|
|
|
585
|
-
local inst =
|
|
452
|
+
local inst = controller.newInstance()
|
|
586
453
|
|
|
587
454
|
local count = 0
|
|
588
455
|
local cleanup = binder:ObserveInstance(inst, function()
|
|
@@ -595,34 +462,34 @@ describe("Binder:ObserveInstance()", function()
|
|
|
595
462
|
|
|
596
463
|
expect(count).toEqual(0)
|
|
597
464
|
|
|
598
|
-
|
|
465
|
+
controller.destroy()
|
|
599
466
|
end)
|
|
600
467
|
|
|
601
468
|
it("throws for a non-instance or non-function", function()
|
|
602
|
-
local
|
|
469
|
+
local controller = setup()
|
|
603
470
|
|
|
604
|
-
local binder =
|
|
605
|
-
|
|
471
|
+
local binder = controller.addBinder(function() end)
|
|
472
|
+
controller.boot()
|
|
606
473
|
|
|
607
474
|
expect(function()
|
|
608
475
|
binder:ObserveInstance(5 :: any, function() end)
|
|
609
476
|
end).toThrow()
|
|
610
477
|
expect(function()
|
|
611
|
-
binder:ObserveInstance(
|
|
478
|
+
binder:ObserveInstance(controller.newInstance(), 5 :: any)
|
|
612
479
|
end).toThrow()
|
|
613
480
|
|
|
614
|
-
|
|
481
|
+
controller.destroy()
|
|
615
482
|
end)
|
|
616
483
|
end)
|
|
617
484
|
|
|
618
485
|
describe("Binder:Observe()", function()
|
|
619
486
|
it("emits the current value on subscribe and updates on bind", function()
|
|
620
|
-
local
|
|
487
|
+
local controller = setup()
|
|
621
488
|
|
|
622
|
-
local binder =
|
|
623
|
-
|
|
489
|
+
local binder = controller.addBinder(makeTrackingClass())
|
|
490
|
+
controller.boot()
|
|
624
491
|
|
|
625
|
-
local inst =
|
|
492
|
+
local inst = controller.newInstance()
|
|
626
493
|
|
|
627
494
|
-- Wrap each emission so the initial nil value is still recorded.
|
|
628
495
|
local emissions = {}
|
|
@@ -630,7 +497,6 @@ describe("Binder:Observe()", function()
|
|
|
630
497
|
table.insert(emissions, { value = class })
|
|
631
498
|
end)
|
|
632
499
|
|
|
633
|
-
-- First emission is the (nil) current value.
|
|
634
500
|
expect(#emissions).toEqual(1)
|
|
635
501
|
expect(emissions[1].value).toBeNil()
|
|
636
502
|
|
|
@@ -641,81 +507,80 @@ describe("Binder:Observe()", function()
|
|
|
641
507
|
expect(emissions[2].value).toEqual(binder:Get(inst))
|
|
642
508
|
|
|
643
509
|
sub:Destroy()
|
|
644
|
-
|
|
510
|
+
controller.destroy()
|
|
645
511
|
end)
|
|
646
512
|
end)
|
|
647
513
|
|
|
648
514
|
describe("Binder:Promise()", function()
|
|
649
515
|
it("resolves immediately when already bound", function()
|
|
650
|
-
local
|
|
516
|
+
local controller = setup()
|
|
651
517
|
|
|
652
|
-
local binder =
|
|
518
|
+
local binder = controller.addBinder(makeTrackingClass())
|
|
653
519
|
|
|
654
|
-
local inst =
|
|
520
|
+
local inst = controller.newInstance()
|
|
655
521
|
binder:Tag(inst)
|
|
656
|
-
|
|
522
|
+
controller.boot()
|
|
657
523
|
|
|
658
524
|
local promise = binder:Promise(inst)
|
|
659
525
|
expect(promise:IsFulfilled()).toEqual(true)
|
|
660
526
|
|
|
661
|
-
|
|
527
|
+
controller.destroy()
|
|
662
528
|
end)
|
|
663
529
|
|
|
664
530
|
it("throws when passed a non-instance", function()
|
|
665
|
-
local
|
|
531
|
+
local controller = setup()
|
|
666
532
|
|
|
667
|
-
local binder =
|
|
668
|
-
|
|
533
|
+
local binder = controller.addBinder(function() end)
|
|
534
|
+
controller.boot()
|
|
669
535
|
|
|
670
536
|
expect(function()
|
|
671
537
|
binder:Promise(5 :: any)
|
|
672
538
|
end).toThrow()
|
|
673
539
|
|
|
674
|
-
|
|
540
|
+
controller.destroy()
|
|
675
541
|
end)
|
|
676
542
|
end)
|
|
677
543
|
|
|
678
544
|
describe("Binder:_add() dedupe", function()
|
|
679
545
|
it("does not rebind an already-bound instance", function()
|
|
680
|
-
local
|
|
546
|
+
local controller = setup()
|
|
681
547
|
|
|
682
548
|
local constructed = 0
|
|
683
|
-
local binder =
|
|
549
|
+
local binder = controller.addBinder(function(inst)
|
|
684
550
|
constructed += 1
|
|
685
551
|
return { instance = inst }
|
|
686
552
|
end)
|
|
687
|
-
|
|
553
|
+
controller.boot()
|
|
688
554
|
|
|
689
|
-
local inst =
|
|
555
|
+
local inst = controller.newInstance()
|
|
690
556
|
binder:Tag(inst)
|
|
691
557
|
assert((binder:Promise(inst):Yield()), "Never bound")
|
|
692
558
|
|
|
693
|
-
-- Re-tagging an already-tagged instance must not reconstruct.
|
|
694
559
|
binder:Tag(inst)
|
|
695
560
|
task.wait()
|
|
696
561
|
|
|
697
562
|
expect(constructed).toEqual(1)
|
|
698
563
|
|
|
699
|
-
|
|
564
|
+
controller.destroy()
|
|
700
565
|
end)
|
|
701
566
|
end)
|
|
702
567
|
|
|
703
568
|
describe("Binder teardown", function()
|
|
704
569
|
it("removes and destroys all bound classes when the service bag is destroyed", function()
|
|
705
|
-
local
|
|
570
|
+
local controller = setup()
|
|
706
571
|
|
|
707
|
-
local binder =
|
|
708
|
-
|
|
572
|
+
local binder = controller.addBinder(makeTrackingClass())
|
|
573
|
+
controller.boot()
|
|
709
574
|
|
|
710
|
-
local inst =
|
|
575
|
+
local inst = controller.newInstance()
|
|
711
576
|
binder:Tag(inst)
|
|
712
577
|
local ok, class = binder:Promise(inst):Yield()
|
|
713
578
|
assert(ok, "Never bound")
|
|
714
579
|
|
|
715
|
-
|
|
580
|
+
controller.destroyServiceBag()
|
|
716
581
|
|
|
717
582
|
expect(class.destroyed).toEqual(true)
|
|
718
583
|
|
|
719
|
-
|
|
584
|
+
controller.destroy()
|
|
720
585
|
end)
|
|
721
586
|
end)
|