@quenty/binder 14.39.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 +10 -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 +586 -0
- package/src/Shared/BinderGroup.spec.lua +151 -0
- package/src/Shared/BinderGroupProvider.spec.lua +117 -0
- package/src/Shared/BinderTestUtils.lua +163 -0
- package/src/Shared/BinderUtils.spec.lua +338 -0
- package/src/Shared/Collection/BoundChildCollection.spec.lua +203 -0
- package/src/Shared/Promise/promiseBoundClass.spec.lua +129 -0
- package/src/Shared/Trackers/BoundAncestorTracker.spec.lua +167 -0
- package/src/Shared/Trackers/BoundParentTracker.spec.lua +159 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--[[
|
|
3
|
+
@class BoundAncestorTracker.spec.lua
|
|
4
|
+
]]
|
|
5
|
+
|
|
6
|
+
local require = require(script.Parent.loader).load(script)
|
|
7
|
+
|
|
8
|
+
local Binder = require("Binder")
|
|
9
|
+
local BinderProvider = require("BinderProvider")
|
|
10
|
+
local BoundAncestorTracker = require("BoundAncestorTracker")
|
|
11
|
+
local Jest = require("Jest")
|
|
12
|
+
local ServiceBag = require("ServiceBag")
|
|
13
|
+
|
|
14
|
+
local describe = Jest.Globals.describe
|
|
15
|
+
local expect = Jest.Globals.expect
|
|
16
|
+
local it = Jest.Globals.it
|
|
17
|
+
|
|
18
|
+
local specCounter = 0
|
|
19
|
+
|
|
20
|
+
local function makeClass()
|
|
21
|
+
local Class = {}
|
|
22
|
+
Class.__index = Class
|
|
23
|
+
Class.ClassName = "BoundAncestorTrackerSpecClass"
|
|
24
|
+
function Class.new(inst)
|
|
25
|
+
return setmetatable({ instance = inst }, Class)
|
|
26
|
+
end
|
|
27
|
+
function Class:Destroy() end
|
|
28
|
+
return Class
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
local function setup()
|
|
32
|
+
specCounter += 1
|
|
33
|
+
local suffix = specCounter
|
|
34
|
+
|
|
35
|
+
local serviceBag = ServiceBag.new()
|
|
36
|
+
local container = Instance.new("Folder")
|
|
37
|
+
container.Name = "BoundAncestorTrackerSpecContainer"
|
|
38
|
+
container.Parent = workspace
|
|
39
|
+
|
|
40
|
+
local instances = {}
|
|
41
|
+
local cleanups = {}
|
|
42
|
+
local booted = false
|
|
43
|
+
|
|
44
|
+
local binder = Binder.new(string.format("BoundAncestorTrackerSpecTag_%d", suffix), makeClass() :: any)
|
|
45
|
+
|
|
46
|
+
local function newInstance(parent: Instance?): Instance
|
|
47
|
+
local inst = Instance.new("Folder")
|
|
48
|
+
inst.Parent = parent or container
|
|
49
|
+
table.insert(instances, inst)
|
|
50
|
+
return inst
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
local function track(item: any): any
|
|
54
|
+
table.insert(cleanups, item)
|
|
55
|
+
return item
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
local function boot()
|
|
59
|
+
assert(not booted, "Already booted")
|
|
60
|
+
booted = true
|
|
61
|
+
|
|
62
|
+
local provider = BinderProvider.new(string.format("BoundAncestorTrackerSpecProvider_%d", suffix), function(self)
|
|
63
|
+
self:Add(binder)
|
|
64
|
+
end)
|
|
65
|
+
serviceBag:GetService(provider)
|
|
66
|
+
serviceBag:Init()
|
|
67
|
+
serviceBag:Start()
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
local function awaitChange(tracker, previous)
|
|
71
|
+
if tracker.Class.Value == previous then
|
|
72
|
+
tracker.Class.Changed:Wait()
|
|
73
|
+
end
|
|
74
|
+
return tracker.Class.Value
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
container = container,
|
|
79
|
+
binder = binder,
|
|
80
|
+
newInstance = newInstance,
|
|
81
|
+
track = track,
|
|
82
|
+
boot = boot,
|
|
83
|
+
awaitChange = awaitChange,
|
|
84
|
+
destroy = function()
|
|
85
|
+
for _, item in cleanups do
|
|
86
|
+
pcall(function()
|
|
87
|
+
item:Destroy()
|
|
88
|
+
end)
|
|
89
|
+
end
|
|
90
|
+
serviceBag:Destroy()
|
|
91
|
+
for _, inst in instances do
|
|
92
|
+
pcall(function()
|
|
93
|
+
inst:Destroy()
|
|
94
|
+
end)
|
|
95
|
+
end
|
|
96
|
+
container:Destroy()
|
|
97
|
+
end,
|
|
98
|
+
}
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
describe("BoundAncestorTracker tracking", function()
|
|
102
|
+
it("exposes the nearest bound ancestor's class", function()
|
|
103
|
+
local controller = setup()
|
|
104
|
+
|
|
105
|
+
local grandparent = controller.newInstance()
|
|
106
|
+
local parent = controller.newInstance(grandparent)
|
|
107
|
+
local child = controller.newInstance(parent)
|
|
108
|
+
controller.binder:Tag(grandparent)
|
|
109
|
+
controller.boot()
|
|
110
|
+
|
|
111
|
+
local tracker = controller.track(BoundAncestorTracker.new(controller.binder, child))
|
|
112
|
+
expect(tracker.Class.Value).toEqual(controller.binder:Get(grandparent))
|
|
113
|
+
|
|
114
|
+
controller.destroy()
|
|
115
|
+
end)
|
|
116
|
+
|
|
117
|
+
it("has no value when no ancestor is bound", function()
|
|
118
|
+
local controller = setup()
|
|
119
|
+
|
|
120
|
+
local parent = controller.newInstance()
|
|
121
|
+
local child = controller.newInstance(parent)
|
|
122
|
+
controller.boot()
|
|
123
|
+
|
|
124
|
+
local tracker = controller.track(BoundAncestorTracker.new(controller.binder, child))
|
|
125
|
+
expect(tracker.Class.Value).toBeNil()
|
|
126
|
+
|
|
127
|
+
controller.destroy()
|
|
128
|
+
end)
|
|
129
|
+
|
|
130
|
+
it("updates when an ancestor becomes bound", function()
|
|
131
|
+
local controller = setup()
|
|
132
|
+
|
|
133
|
+
-- The tracker resolves ancestors above the child's direct parent, so bind the grandparent.
|
|
134
|
+
local ancestor = controller.newInstance()
|
|
135
|
+
local parent = controller.newInstance(ancestor)
|
|
136
|
+
local child = controller.newInstance(parent)
|
|
137
|
+
controller.boot()
|
|
138
|
+
|
|
139
|
+
local tracker = controller.track(BoundAncestorTracker.new(controller.binder, child))
|
|
140
|
+
expect(tracker.Class.Value).toBeNil()
|
|
141
|
+
|
|
142
|
+
controller.binder:Tag(ancestor)
|
|
143
|
+
local value = controller.awaitChange(tracker, nil)
|
|
144
|
+
expect(value).toEqual(controller.binder:Get(ancestor))
|
|
145
|
+
|
|
146
|
+
controller.destroy()
|
|
147
|
+
end)
|
|
148
|
+
|
|
149
|
+
it("clears the value when the child leaves the bound ancestry", function()
|
|
150
|
+
local controller = setup()
|
|
151
|
+
|
|
152
|
+
local ancestor = controller.newInstance()
|
|
153
|
+
local parent = controller.newInstance(ancestor)
|
|
154
|
+
local child = controller.newInstance(parent)
|
|
155
|
+
controller.binder:Tag(ancestor)
|
|
156
|
+
controller.boot()
|
|
157
|
+
|
|
158
|
+
local tracker = controller.track(BoundAncestorTracker.new(controller.binder, child))
|
|
159
|
+
local class = controller.binder:Get(ancestor)
|
|
160
|
+
expect(tracker.Class.Value).toEqual(class)
|
|
161
|
+
|
|
162
|
+
child.Parent = controller.container
|
|
163
|
+
expect(controller.awaitChange(tracker, class)).toBeNil()
|
|
164
|
+
|
|
165
|
+
controller.destroy()
|
|
166
|
+
end)
|
|
167
|
+
end)
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--[[
|
|
3
|
+
@class BoundParentTracker.spec.lua
|
|
4
|
+
]]
|
|
5
|
+
|
|
6
|
+
local require = require(script.Parent.loader).load(script)
|
|
7
|
+
|
|
8
|
+
local Binder = require("Binder")
|
|
9
|
+
local BinderProvider = require("BinderProvider")
|
|
10
|
+
local BoundParentTracker = require("BoundParentTracker")
|
|
11
|
+
local Jest = require("Jest")
|
|
12
|
+
local ServiceBag = require("ServiceBag")
|
|
13
|
+
|
|
14
|
+
local describe = Jest.Globals.describe
|
|
15
|
+
local expect = Jest.Globals.expect
|
|
16
|
+
local it = Jest.Globals.it
|
|
17
|
+
|
|
18
|
+
local specCounter = 0
|
|
19
|
+
|
|
20
|
+
local function makeClass()
|
|
21
|
+
local Class = {}
|
|
22
|
+
Class.__index = Class
|
|
23
|
+
Class.ClassName = "BoundParentTrackerSpecClass"
|
|
24
|
+
function Class.new(inst)
|
|
25
|
+
return setmetatable({ instance = inst }, Class)
|
|
26
|
+
end
|
|
27
|
+
function Class:Destroy() end
|
|
28
|
+
return Class
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
local function setup()
|
|
32
|
+
specCounter += 1
|
|
33
|
+
local suffix = specCounter
|
|
34
|
+
|
|
35
|
+
local serviceBag = ServiceBag.new()
|
|
36
|
+
local container = Instance.new("Folder")
|
|
37
|
+
container.Name = "BoundParentTrackerSpecContainer"
|
|
38
|
+
container.Parent = workspace
|
|
39
|
+
|
|
40
|
+
local instances = {}
|
|
41
|
+
local cleanups = {}
|
|
42
|
+
local booted = false
|
|
43
|
+
|
|
44
|
+
local binder = Binder.new(string.format("BoundParentTrackerSpecTag_%d", suffix), makeClass() :: any)
|
|
45
|
+
|
|
46
|
+
local function newInstance(parent: Instance?): Instance
|
|
47
|
+
local inst = Instance.new("Folder")
|
|
48
|
+
inst.Parent = parent or container
|
|
49
|
+
table.insert(instances, inst)
|
|
50
|
+
return inst
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
local function track(item: any): any
|
|
54
|
+
table.insert(cleanups, item)
|
|
55
|
+
return item
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
local function boot()
|
|
59
|
+
assert(not booted, "Already booted")
|
|
60
|
+
booted = true
|
|
61
|
+
|
|
62
|
+
local provider = BinderProvider.new(string.format("BoundParentTrackerSpecProvider_%d", suffix), function(self)
|
|
63
|
+
self:Add(binder)
|
|
64
|
+
end)
|
|
65
|
+
serviceBag:GetService(provider)
|
|
66
|
+
serviceBag:Init()
|
|
67
|
+
serviceBag:Start()
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
local function awaitChange(tracker, previous)
|
|
71
|
+
if tracker.Class.Value == previous then
|
|
72
|
+
tracker.Class.Changed:Wait()
|
|
73
|
+
end
|
|
74
|
+
return tracker.Class.Value
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
container = container,
|
|
79
|
+
binder = binder,
|
|
80
|
+
newInstance = newInstance,
|
|
81
|
+
track = track,
|
|
82
|
+
boot = boot,
|
|
83
|
+
awaitChange = awaitChange,
|
|
84
|
+
destroy = function()
|
|
85
|
+
for _, item in cleanups do
|
|
86
|
+
pcall(function()
|
|
87
|
+
item:Destroy()
|
|
88
|
+
end)
|
|
89
|
+
end
|
|
90
|
+
serviceBag:Destroy()
|
|
91
|
+
for _, inst in instances do
|
|
92
|
+
pcall(function()
|
|
93
|
+
inst:Destroy()
|
|
94
|
+
end)
|
|
95
|
+
end
|
|
96
|
+
container:Destroy()
|
|
97
|
+
end,
|
|
98
|
+
}
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
describe("BoundParentTracker.new()", function()
|
|
102
|
+
it("throws without a binder or child", function()
|
|
103
|
+
expect(function()
|
|
104
|
+
BoundParentTracker.new(nil :: any, Instance.new("Folder"))
|
|
105
|
+
end).toThrow()
|
|
106
|
+
end)
|
|
107
|
+
end)
|
|
108
|
+
|
|
109
|
+
describe("BoundParentTracker tracking", function()
|
|
110
|
+
it("exposes the bound class of the direct parent", function()
|
|
111
|
+
local controller = setup()
|
|
112
|
+
|
|
113
|
+
local parent = controller.newInstance()
|
|
114
|
+
local child = controller.newInstance(parent)
|
|
115
|
+
controller.binder:Tag(parent)
|
|
116
|
+
controller.boot()
|
|
117
|
+
|
|
118
|
+
local tracker = controller.track(BoundParentTracker.new(controller.binder, child))
|
|
119
|
+
expect(tracker.Class.Value).toEqual(controller.binder:Get(parent))
|
|
120
|
+
|
|
121
|
+
controller.destroy()
|
|
122
|
+
end)
|
|
123
|
+
|
|
124
|
+
it("clears the value when the child is reparented off the bound parent", function()
|
|
125
|
+
local controller = setup()
|
|
126
|
+
|
|
127
|
+
local parent = controller.newInstance()
|
|
128
|
+
local child = controller.newInstance(parent)
|
|
129
|
+
controller.binder:Tag(parent)
|
|
130
|
+
controller.boot()
|
|
131
|
+
|
|
132
|
+
local tracker = controller.track(BoundParentTracker.new(controller.binder, child))
|
|
133
|
+
local class = controller.binder:Get(parent)
|
|
134
|
+
expect(tracker.Class.Value).toEqual(class)
|
|
135
|
+
|
|
136
|
+
child.Parent = controller.container
|
|
137
|
+
expect(controller.awaitChange(tracker, class)).toBeNil()
|
|
138
|
+
|
|
139
|
+
controller.destroy()
|
|
140
|
+
end)
|
|
141
|
+
|
|
142
|
+
it("clears the value when the parent's class is unbound", function()
|
|
143
|
+
local controller = setup()
|
|
144
|
+
|
|
145
|
+
local parent = controller.newInstance()
|
|
146
|
+
local child = controller.newInstance(parent)
|
|
147
|
+
controller.binder:Tag(parent)
|
|
148
|
+
controller.boot()
|
|
149
|
+
|
|
150
|
+
local tracker = controller.track(BoundParentTracker.new(controller.binder, child))
|
|
151
|
+
local class = controller.binder:Get(parent)
|
|
152
|
+
expect(tracker.Class.Value).toEqual(class)
|
|
153
|
+
|
|
154
|
+
controller.binder:Untag(parent)
|
|
155
|
+
expect(controller.awaitChange(tracker, class)).toBeNil()
|
|
156
|
+
|
|
157
|
+
controller.destroy()
|
|
158
|
+
end)
|
|
159
|
+
end)
|