@quenty/binder 14.38.0 → 14.40.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 +12 -12
- package/src/Shared/Binder.spec.lua +721 -0
- package/src/Shared/BinderGroup.spec.lua +156 -0
- package/src/Shared/BinderGroupProvider.spec.lua +117 -0
- package/src/Shared/BinderProvider.spec.lua +3 -5
- package/src/Shared/BinderUtils.spec.lua +347 -0
- package/src/Shared/Collection/BoundChildCollection.spec.lua +212 -0
- package/src/Shared/Promise/promiseBoundClass.spec.lua +135 -0
- package/src/Shared/Trackers/BoundAncestorTracker.spec.lua +175 -0
- package/src/Shared/Trackers/BoundParentTracker.spec.lua +167 -0
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--[[
|
|
3
|
+
Coverage for BinderGroup: grouping binders, optional constructor validation, and the
|
|
4
|
+
BinderAdded signal. The binders here are never started, so no CollectionService state is
|
|
5
|
+
touched; each is still destroyed through the setup() controller to avoid leaking into the
|
|
6
|
+
shared test place.
|
|
7
|
+
|
|
8
|
+
@class BinderGroup.spec.lua
|
|
9
|
+
]]
|
|
10
|
+
|
|
11
|
+
local require = require(script.Parent.loader).load(script)
|
|
12
|
+
|
|
13
|
+
local Binder = require("Binder")
|
|
14
|
+
local BinderGroup = require("BinderGroup")
|
|
15
|
+
local Jest = require("Jest")
|
|
16
|
+
|
|
17
|
+
local describe = Jest.Globals.describe
|
|
18
|
+
local expect = Jest.Globals.expect
|
|
19
|
+
local it = Jest.Globals.it
|
|
20
|
+
|
|
21
|
+
local tagCounter = 0
|
|
22
|
+
|
|
23
|
+
local function setup()
|
|
24
|
+
local binders: { any } = {}
|
|
25
|
+
|
|
26
|
+
local function newBinder(): Binder.Binder<any>
|
|
27
|
+
tagCounter += 1
|
|
28
|
+
local binder = Binder.new(string.format("BinderGroupSpecTag_%d", tagCounter), function()
|
|
29
|
+
return {}
|
|
30
|
+
end)
|
|
31
|
+
table.insert(binders, binder)
|
|
32
|
+
return binder
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
newBinder = newBinder,
|
|
37
|
+
destroy = function()
|
|
38
|
+
for _, binder in binders do
|
|
39
|
+
pcall(function()
|
|
40
|
+
binder:Destroy()
|
|
41
|
+
end)
|
|
42
|
+
end
|
|
43
|
+
end,
|
|
44
|
+
}
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
describe("BinderGroup.new()", function()
|
|
48
|
+
it("constructs with an initial list of binders", function()
|
|
49
|
+
local env = setup()
|
|
50
|
+
|
|
51
|
+
local binderA = env.newBinder()
|
|
52
|
+
local binderB = env.newBinder()
|
|
53
|
+
local group = BinderGroup.new({ binderA, binderB })
|
|
54
|
+
|
|
55
|
+
expect(#group:GetBinders()).toEqual(2)
|
|
56
|
+
|
|
57
|
+
env.destroy()
|
|
58
|
+
end)
|
|
59
|
+
|
|
60
|
+
it("constructs empty", function()
|
|
61
|
+
local group = BinderGroup.new({})
|
|
62
|
+
expect(#group:GetBinders()).toEqual(0)
|
|
63
|
+
end)
|
|
64
|
+
end)
|
|
65
|
+
|
|
66
|
+
describe("BinderGroup:Add()", function()
|
|
67
|
+
it("adds a binder and exposes it via GetBinders", function()
|
|
68
|
+
local env = setup()
|
|
69
|
+
|
|
70
|
+
local group = BinderGroup.new({})
|
|
71
|
+
local binder = env.newBinder()
|
|
72
|
+
group:Add(binder)
|
|
73
|
+
|
|
74
|
+
expect(group:GetBinders()[1]).toEqual(binder)
|
|
75
|
+
|
|
76
|
+
env.destroy()
|
|
77
|
+
end)
|
|
78
|
+
|
|
79
|
+
it("fires BinderAdded with the added binder", function()
|
|
80
|
+
local env = setup()
|
|
81
|
+
|
|
82
|
+
local group = BinderGroup.new({})
|
|
83
|
+
local fired
|
|
84
|
+
group.BinderAdded:Connect(function(binder)
|
|
85
|
+
fired = binder
|
|
86
|
+
end)
|
|
87
|
+
|
|
88
|
+
local binder = env.newBinder()
|
|
89
|
+
group:Add(binder)
|
|
90
|
+
|
|
91
|
+
expect(fired).toEqual(binder)
|
|
92
|
+
|
|
93
|
+
env.destroy()
|
|
94
|
+
end)
|
|
95
|
+
|
|
96
|
+
it("throws when the value is not a binder", function()
|
|
97
|
+
local group = BinderGroup.new({})
|
|
98
|
+
expect(function()
|
|
99
|
+
group:Add({} :: any)
|
|
100
|
+
end).toThrow()
|
|
101
|
+
end)
|
|
102
|
+
end)
|
|
103
|
+
|
|
104
|
+
describe("BinderGroup constructor validation", function()
|
|
105
|
+
it("accepts binders whose constructor passes validation", function()
|
|
106
|
+
local env = setup()
|
|
107
|
+
|
|
108
|
+
local validated = {}
|
|
109
|
+
local group = BinderGroup.new({}, function(constructor)
|
|
110
|
+
table.insert(validated, constructor)
|
|
111
|
+
return true
|
|
112
|
+
end)
|
|
113
|
+
|
|
114
|
+
local binder = env.newBinder()
|
|
115
|
+
group:Add(binder)
|
|
116
|
+
|
|
117
|
+
expect(validated[1]).toEqual(binder:GetConstructor())
|
|
118
|
+
|
|
119
|
+
env.destroy()
|
|
120
|
+
end)
|
|
121
|
+
|
|
122
|
+
it("throws when the constructor fails validation", function()
|
|
123
|
+
local env = setup()
|
|
124
|
+
|
|
125
|
+
local group = BinderGroup.new({}, function()
|
|
126
|
+
return false
|
|
127
|
+
end)
|
|
128
|
+
|
|
129
|
+
local binder = env.newBinder()
|
|
130
|
+
expect(function()
|
|
131
|
+
group:Add(binder)
|
|
132
|
+
end).toThrow()
|
|
133
|
+
|
|
134
|
+
env.destroy()
|
|
135
|
+
end)
|
|
136
|
+
end)
|
|
137
|
+
|
|
138
|
+
describe("BinderGroup:AddList()", function()
|
|
139
|
+
it("adds each binder in the list", function()
|
|
140
|
+
local env = setup()
|
|
141
|
+
|
|
142
|
+
local group = BinderGroup.new({})
|
|
143
|
+
group:AddList({ env.newBinder(), env.newBinder() })
|
|
144
|
+
|
|
145
|
+
expect(#group:GetBinders()).toEqual(2)
|
|
146
|
+
|
|
147
|
+
env.destroy()
|
|
148
|
+
end)
|
|
149
|
+
|
|
150
|
+
it("throws on a non-table argument", function()
|
|
151
|
+
local group = BinderGroup.new({})
|
|
152
|
+
expect(function()
|
|
153
|
+
group:AddList(5 :: any)
|
|
154
|
+
end).toThrow()
|
|
155
|
+
end)
|
|
156
|
+
end)
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--[[
|
|
3
|
+
Coverage for BinderGroupProvider: registration before init, the groups-added promise, and
|
|
4
|
+
the custom __index guard that rejects unknown lookups.
|
|
5
|
+
|
|
6
|
+
@class BinderGroupProvider.spec.lua
|
|
7
|
+
]]
|
|
8
|
+
|
|
9
|
+
local require = require(script.Parent.loader).load(script)
|
|
10
|
+
|
|
11
|
+
local BinderGroup = require("BinderGroup")
|
|
12
|
+
local BinderGroupProvider = require("BinderGroupProvider")
|
|
13
|
+
local Jest = require("Jest")
|
|
14
|
+
|
|
15
|
+
local describe = Jest.Globals.describe
|
|
16
|
+
local expect = Jest.Globals.expect
|
|
17
|
+
local it = Jest.Globals.it
|
|
18
|
+
|
|
19
|
+
describe("BinderGroupProvider.new()", function()
|
|
20
|
+
it("constructs a provider", function()
|
|
21
|
+
local provider = BinderGroupProvider.new(function() end)
|
|
22
|
+
expect((provider :: any).ClassName).toEqual("BinderGroupProvider")
|
|
23
|
+
end)
|
|
24
|
+
|
|
25
|
+
it("throws without an init method", function()
|
|
26
|
+
expect(function()
|
|
27
|
+
BinderGroupProvider.new(nil :: any)
|
|
28
|
+
end).toThrow()
|
|
29
|
+
end)
|
|
30
|
+
end)
|
|
31
|
+
|
|
32
|
+
describe("BinderGroupProvider:Init()", function()
|
|
33
|
+
it("runs the init method and resolves the groups-added promise", function()
|
|
34
|
+
local ran = false
|
|
35
|
+
local provider = BinderGroupProvider.new(function(self)
|
|
36
|
+
ran = true
|
|
37
|
+
self:Add("Group", BinderGroup.new({}))
|
|
38
|
+
end)
|
|
39
|
+
|
|
40
|
+
expect(provider:PromiseGroupsAdded():IsPending()).toEqual(true)
|
|
41
|
+
provider:Init()
|
|
42
|
+
|
|
43
|
+
expect(ran).toEqual(true)
|
|
44
|
+
expect(provider:PromiseGroupsAdded():IsFulfilled()).toEqual(true)
|
|
45
|
+
end)
|
|
46
|
+
|
|
47
|
+
it("passes extra Init args to the init method", function()
|
|
48
|
+
local received
|
|
49
|
+
local provider = BinderGroupProvider.new(function(_self, arg)
|
|
50
|
+
received = arg
|
|
51
|
+
end)
|
|
52
|
+
provider:Init(4321)
|
|
53
|
+
expect(received).toEqual(4321)
|
|
54
|
+
end)
|
|
55
|
+
|
|
56
|
+
it("throws when initialized twice", function()
|
|
57
|
+
local provider = BinderGroupProvider.new(function() end)
|
|
58
|
+
provider:Init()
|
|
59
|
+
expect(function()
|
|
60
|
+
provider:Init()
|
|
61
|
+
end).toThrow()
|
|
62
|
+
end)
|
|
63
|
+
end)
|
|
64
|
+
|
|
65
|
+
describe("BinderGroupProvider:Add() / Get()", function()
|
|
66
|
+
it("retrieves an added group by name", function()
|
|
67
|
+
local group = BinderGroup.new({})
|
|
68
|
+
local provider = BinderGroupProvider.new(function(self)
|
|
69
|
+
self:Add("MyGroup", group)
|
|
70
|
+
end)
|
|
71
|
+
provider:Init()
|
|
72
|
+
|
|
73
|
+
expect(provider:Get("MyGroup")).toEqual(group)
|
|
74
|
+
end)
|
|
75
|
+
|
|
76
|
+
it("returns nil for an unknown group name via Get()", function()
|
|
77
|
+
local provider = BinderGroupProvider.new(function() end)
|
|
78
|
+
provider:Init()
|
|
79
|
+
expect(provider:Get("Missing")).toBeNil()
|
|
80
|
+
end)
|
|
81
|
+
|
|
82
|
+
it("throws when adding a duplicate group name", function()
|
|
83
|
+
local provider = BinderGroupProvider.new(function(self)
|
|
84
|
+
self:Add("Dup", BinderGroup.new({}))
|
|
85
|
+
self:Add("Dup", BinderGroup.new({}))
|
|
86
|
+
end)
|
|
87
|
+
|
|
88
|
+
expect(function()
|
|
89
|
+
provider:Init()
|
|
90
|
+
end).toThrow()
|
|
91
|
+
end)
|
|
92
|
+
|
|
93
|
+
it("throws when adding after initialization", function()
|
|
94
|
+
local provider = BinderGroupProvider.new(function() end)
|
|
95
|
+
provider:Init()
|
|
96
|
+
expect(function()
|
|
97
|
+
provider:Add("Late", BinderGroup.new({}))
|
|
98
|
+
end).toThrow()
|
|
99
|
+
end)
|
|
100
|
+
|
|
101
|
+
it("throws on a non-string group name", function()
|
|
102
|
+
local provider = BinderGroupProvider.new(function() end)
|
|
103
|
+
expect(function()
|
|
104
|
+
provider:Get(5 :: any)
|
|
105
|
+
end).toThrow()
|
|
106
|
+
end)
|
|
107
|
+
end)
|
|
108
|
+
|
|
109
|
+
describe("BinderGroupProvider __index guard", function()
|
|
110
|
+
it("throws when indexing an unknown key", function()
|
|
111
|
+
local provider = BinderGroupProvider.new(function() end)
|
|
112
|
+
provider:Init()
|
|
113
|
+
expect(function()
|
|
114
|
+
return (provider :: any).SomethingInvalid
|
|
115
|
+
end).toThrow()
|
|
116
|
+
end)
|
|
117
|
+
end)
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
--!
|
|
1
|
+
--!strict
|
|
2
2
|
--[[
|
|
3
3
|
@class BinderProvider.spec.lua
|
|
4
4
|
]]
|
|
5
5
|
|
|
6
|
-
local require = (
|
|
7
|
-
game:GetService("ServerScriptService"):FindFirstChild("LoaderUtils", true).Parent
|
|
8
|
-
).bootstrapStory(script) :: typeof(require(script.Parent.loader).load(script))
|
|
6
|
+
local require = require(script.Parent.loader).load(script)
|
|
9
7
|
|
|
10
8
|
local Binder = require("Binder")
|
|
11
9
|
local BinderProvider = require("BinderProvider")
|
|
@@ -49,7 +47,7 @@ describe("BinderProvider.new()", function()
|
|
|
49
47
|
|
|
50
48
|
provider:Init()
|
|
51
49
|
|
|
52
|
-
expect(provider.Test).toEqual(expect.any("table"))
|
|
50
|
+
expect((provider :: any).Test).toEqual(expect.any("table"))
|
|
53
51
|
|
|
54
52
|
provider:Destroy()
|
|
55
53
|
if binder then
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--[[
|
|
3
|
+
Coverage for BinderUtils lookup helpers.
|
|
4
|
+
|
|
5
|
+
Binders are booted through a ServiceBag (as production code does) and instances are tagged
|
|
6
|
+
BEFORE the service bag starts, so they bind synchronously and :Get() resolves without waiting.
|
|
7
|
+
Instances live in a workspace container so the binders' added signals fire. Each test tears
|
|
8
|
+
down its service bag and instances because the test place is shared across a batch run.
|
|
9
|
+
|
|
10
|
+
@class BinderUtils.spec.lua
|
|
11
|
+
]]
|
|
12
|
+
|
|
13
|
+
local require = require(script.Parent.loader).load(script)
|
|
14
|
+
|
|
15
|
+
local Binder = require("Binder")
|
|
16
|
+
local BinderProvider = require("BinderProvider")
|
|
17
|
+
local BinderUtils = require("BinderUtils")
|
|
18
|
+
local Jest = require("Jest")
|
|
19
|
+
local ServiceBag = require("ServiceBag")
|
|
20
|
+
|
|
21
|
+
local describe = Jest.Globals.describe
|
|
22
|
+
local expect = Jest.Globals.expect
|
|
23
|
+
local it = Jest.Globals.it
|
|
24
|
+
|
|
25
|
+
local specCounter = 0
|
|
26
|
+
|
|
27
|
+
local function makeClass()
|
|
28
|
+
local Class = {}
|
|
29
|
+
Class.__index = Class
|
|
30
|
+
Class.ClassName = "BinderUtilsSpecClass"
|
|
31
|
+
function Class.new(inst)
|
|
32
|
+
return setmetatable({ instance = inst }, Class)
|
|
33
|
+
end
|
|
34
|
+
function Class:Destroy() end
|
|
35
|
+
return Class
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
local function setup()
|
|
39
|
+
specCounter += 1
|
|
40
|
+
local suffix = specCounter
|
|
41
|
+
|
|
42
|
+
local serviceBag = ServiceBag.new()
|
|
43
|
+
local container = Instance.new("Folder")
|
|
44
|
+
container.Name = "BinderUtilsSpecContainer"
|
|
45
|
+
container.Parent = workspace
|
|
46
|
+
|
|
47
|
+
local instances = {}
|
|
48
|
+
local pendingBinders: { any } = {}
|
|
49
|
+
local tagCounter = 0
|
|
50
|
+
local booted = false
|
|
51
|
+
|
|
52
|
+
local function newBinder(): Binder.Binder<any>
|
|
53
|
+
assert(not booted, "Cannot add a binder after boot()")
|
|
54
|
+
tagCounter += 1
|
|
55
|
+
local binder = Binder.new(string.format("BinderUtilsSpecTag_%d_%d", suffix, tagCounter), makeClass() :: any)
|
|
56
|
+
table.insert(pendingBinders, binder)
|
|
57
|
+
return binder
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
local function newInstance(parent: Instance?, className: string?): Instance
|
|
61
|
+
local inst = Instance.new(className or "Folder")
|
|
62
|
+
inst.Parent = parent or container
|
|
63
|
+
table.insert(instances, inst)
|
|
64
|
+
return inst
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
-- Tags each instance with the binder, then boots everything through the service bag so the
|
|
68
|
+
-- instances bind synchronously.
|
|
69
|
+
local function boot()
|
|
70
|
+
assert(not booted, "Already booted")
|
|
71
|
+
booted = true
|
|
72
|
+
|
|
73
|
+
local binders = pendingBinders
|
|
74
|
+
local provider = BinderProvider.new(string.format("BinderUtilsSpecProvider_%d", suffix), function(self)
|
|
75
|
+
for _, binder in binders do
|
|
76
|
+
self:Add(binder)
|
|
77
|
+
end
|
|
78
|
+
end)
|
|
79
|
+
serviceBag:GetService(provider)
|
|
80
|
+
serviceBag:Init()
|
|
81
|
+
serviceBag:Start()
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
container = container,
|
|
86
|
+
newBinder = newBinder,
|
|
87
|
+
newInstance = newInstance,
|
|
88
|
+
boot = boot,
|
|
89
|
+
destroy = function()
|
|
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("BinderUtils.findFirstAncestor()", function()
|
|
102
|
+
it("returns the nearest bound ancestor, skipping the child itself", function()
|
|
103
|
+
local env = setup()
|
|
104
|
+
|
|
105
|
+
local grandparent = env.newInstance()
|
|
106
|
+
local parent = env.newInstance(grandparent)
|
|
107
|
+
local child = env.newInstance(parent)
|
|
108
|
+
|
|
109
|
+
local binder = env.newBinder()
|
|
110
|
+
binder:Tag(grandparent)
|
|
111
|
+
binder:Tag(child) -- child is bound too, but must be skipped
|
|
112
|
+
env.boot()
|
|
113
|
+
|
|
114
|
+
expect(BinderUtils.findFirstAncestor(binder, child)).toEqual(binder:Get(grandparent))
|
|
115
|
+
|
|
116
|
+
env.destroy()
|
|
117
|
+
end)
|
|
118
|
+
|
|
119
|
+
it("returns nil when no ancestor is bound", function()
|
|
120
|
+
local env = setup()
|
|
121
|
+
|
|
122
|
+
local parent = env.newInstance()
|
|
123
|
+
local child = env.newInstance(parent)
|
|
124
|
+
local binder = env.newBinder()
|
|
125
|
+
env.boot()
|
|
126
|
+
|
|
127
|
+
expect(BinderUtils.findFirstAncestor(binder, child)).toBeNil()
|
|
128
|
+
|
|
129
|
+
env.destroy()
|
|
130
|
+
end)
|
|
131
|
+
|
|
132
|
+
it("throws for a non-instance child", function()
|
|
133
|
+
local env = setup()
|
|
134
|
+
|
|
135
|
+
local binder = env.newBinder()
|
|
136
|
+
env.boot()
|
|
137
|
+
expect(function()
|
|
138
|
+
BinderUtils.findFirstAncestor(binder, 5 :: any)
|
|
139
|
+
end).toThrow()
|
|
140
|
+
|
|
141
|
+
env.destroy()
|
|
142
|
+
end)
|
|
143
|
+
end)
|
|
144
|
+
|
|
145
|
+
describe("BinderUtils.findFirstChild()", function()
|
|
146
|
+
it("returns the first bound child", function()
|
|
147
|
+
local env = setup()
|
|
148
|
+
|
|
149
|
+
local parent = env.newInstance()
|
|
150
|
+
local unboundChild = env.newInstance(parent)
|
|
151
|
+
local boundChild = env.newInstance(parent)
|
|
152
|
+
|
|
153
|
+
local binder = env.newBinder()
|
|
154
|
+
binder:Tag(boundChild)
|
|
155
|
+
env.boot()
|
|
156
|
+
|
|
157
|
+
expect(BinderUtils.findFirstChild(binder, parent)).toEqual(binder:Get(boundChild))
|
|
158
|
+
expect(binder:Get(unboundChild)).toBeNil()
|
|
159
|
+
|
|
160
|
+
env.destroy()
|
|
161
|
+
end)
|
|
162
|
+
|
|
163
|
+
it("returns nil when no child is bound", function()
|
|
164
|
+
local env = setup()
|
|
165
|
+
|
|
166
|
+
local parent = env.newInstance()
|
|
167
|
+
env.newInstance(parent)
|
|
168
|
+
local binder = env.newBinder()
|
|
169
|
+
env.boot()
|
|
170
|
+
|
|
171
|
+
expect(BinderUtils.findFirstChild(binder, parent)).toBeNil()
|
|
172
|
+
|
|
173
|
+
env.destroy()
|
|
174
|
+
end)
|
|
175
|
+
end)
|
|
176
|
+
|
|
177
|
+
describe("BinderUtils.getChildren()", function()
|
|
178
|
+
it("returns every bound child", function()
|
|
179
|
+
local env = setup()
|
|
180
|
+
|
|
181
|
+
local parent = env.newInstance()
|
|
182
|
+
local childA = env.newInstance(parent)
|
|
183
|
+
local childB = env.newInstance(parent)
|
|
184
|
+
env.newInstance(parent) -- unbound
|
|
185
|
+
|
|
186
|
+
local binder = env.newBinder()
|
|
187
|
+
binder:Tag(childA)
|
|
188
|
+
binder:Tag(childB)
|
|
189
|
+
env.boot()
|
|
190
|
+
|
|
191
|
+
expect(#BinderUtils.getChildren(binder, parent)).toEqual(2)
|
|
192
|
+
|
|
193
|
+
env.destroy()
|
|
194
|
+
end)
|
|
195
|
+
|
|
196
|
+
it("does not include bound descendants deeper than one level", function()
|
|
197
|
+
local env = setup()
|
|
198
|
+
|
|
199
|
+
local parent = env.newInstance()
|
|
200
|
+
local child = env.newInstance(parent)
|
|
201
|
+
local grandchild = env.newInstance(child)
|
|
202
|
+
|
|
203
|
+
local binder = env.newBinder()
|
|
204
|
+
binder:Tag(grandchild)
|
|
205
|
+
env.boot()
|
|
206
|
+
|
|
207
|
+
expect(#BinderUtils.getChildren(binder, parent)).toEqual(0)
|
|
208
|
+
|
|
209
|
+
env.destroy()
|
|
210
|
+
end)
|
|
211
|
+
end)
|
|
212
|
+
|
|
213
|
+
describe("BinderUtils.getDescendants()", function()
|
|
214
|
+
it("returns bound instances at any depth", function()
|
|
215
|
+
local env = setup()
|
|
216
|
+
|
|
217
|
+
local parent = env.newInstance()
|
|
218
|
+
local child = env.newInstance(parent)
|
|
219
|
+
local grandchild = env.newInstance(child)
|
|
220
|
+
|
|
221
|
+
local binder = env.newBinder()
|
|
222
|
+
binder:Tag(child)
|
|
223
|
+
binder:Tag(grandchild)
|
|
224
|
+
env.boot()
|
|
225
|
+
|
|
226
|
+
expect(#BinderUtils.getDescendants(binder, parent)).toEqual(2)
|
|
227
|
+
|
|
228
|
+
env.destroy()
|
|
229
|
+
end)
|
|
230
|
+
end)
|
|
231
|
+
|
|
232
|
+
describe("BinderUtils.mapBinderListToTable()", function()
|
|
233
|
+
it("keys binders by their tag", function()
|
|
234
|
+
local env = setup()
|
|
235
|
+
|
|
236
|
+
local binderA = env.newBinder()
|
|
237
|
+
local binderB = env.newBinder()
|
|
238
|
+
env.boot()
|
|
239
|
+
|
|
240
|
+
local map = BinderUtils.mapBinderListToTable({ binderA, binderB })
|
|
241
|
+
expect(map[binderA:GetTag()]).toEqual(binderA)
|
|
242
|
+
expect(map[binderB:GetTag()]).toEqual(binderB)
|
|
243
|
+
|
|
244
|
+
env.destroy()
|
|
245
|
+
end)
|
|
246
|
+
end)
|
|
247
|
+
|
|
248
|
+
describe("BinderUtils.getMappedFromList()", function()
|
|
249
|
+
it("resolves bound values across an instance list by tag", function()
|
|
250
|
+
local env = setup()
|
|
251
|
+
|
|
252
|
+
local instA = env.newInstance()
|
|
253
|
+
local instB = env.newInstance()
|
|
254
|
+
local binderA = env.newBinder()
|
|
255
|
+
local binderB = env.newBinder()
|
|
256
|
+
binderA:Tag(instA)
|
|
257
|
+
binderB:Tag(instB)
|
|
258
|
+
env.boot()
|
|
259
|
+
|
|
260
|
+
local tagsMap = BinderUtils.mapBinderListToTable({ binderA, binderB })
|
|
261
|
+
local objects = BinderUtils.getMappedFromList(tagsMap, { instA, instB, env.newInstance() })
|
|
262
|
+
|
|
263
|
+
expect(#objects).toEqual(2)
|
|
264
|
+
|
|
265
|
+
env.destroy()
|
|
266
|
+
end)
|
|
267
|
+
end)
|
|
268
|
+
|
|
269
|
+
describe("BinderUtils.getChildrenOfBinders()", function()
|
|
270
|
+
it("returns children bound by any binder in the list", function()
|
|
271
|
+
local env = setup()
|
|
272
|
+
|
|
273
|
+
local parent = env.newInstance()
|
|
274
|
+
local childA = env.newInstance(parent)
|
|
275
|
+
local childB = env.newInstance(parent)
|
|
276
|
+
|
|
277
|
+
local binderA = env.newBinder()
|
|
278
|
+
local binderB = env.newBinder()
|
|
279
|
+
binderA:Tag(childA)
|
|
280
|
+
binderB:Tag(childB)
|
|
281
|
+
env.boot()
|
|
282
|
+
|
|
283
|
+
expect(#BinderUtils.getChildrenOfBinders({ binderA, binderB }, parent)).toEqual(2)
|
|
284
|
+
|
|
285
|
+
env.destroy()
|
|
286
|
+
end)
|
|
287
|
+
end)
|
|
288
|
+
|
|
289
|
+
describe("BinderUtils.getLinkedChildren()", function()
|
|
290
|
+
it("resolves bound targets of matching ObjectValue links", function()
|
|
291
|
+
local env = setup()
|
|
292
|
+
|
|
293
|
+
local parent = env.newInstance()
|
|
294
|
+
local target = env.newInstance()
|
|
295
|
+
local binder = env.newBinder()
|
|
296
|
+
binder:Tag(target)
|
|
297
|
+
|
|
298
|
+
local link = env.newInstance(parent, "ObjectValue") :: ObjectValue
|
|
299
|
+
link.Name = "Link"
|
|
300
|
+
link.Value = target
|
|
301
|
+
env.boot()
|
|
302
|
+
|
|
303
|
+
local objects = BinderUtils.getLinkedChildren(binder, "Link", parent)
|
|
304
|
+
expect(#objects).toEqual(1)
|
|
305
|
+
expect(objects[1]).toEqual(binder:Get(target))
|
|
306
|
+
|
|
307
|
+
env.destroy()
|
|
308
|
+
end)
|
|
309
|
+
|
|
310
|
+
it("ignores links whose name does not match", function()
|
|
311
|
+
local env = setup()
|
|
312
|
+
|
|
313
|
+
local parent = env.newInstance()
|
|
314
|
+
local target = env.newInstance()
|
|
315
|
+
local binder = env.newBinder()
|
|
316
|
+
binder:Tag(target)
|
|
317
|
+
|
|
318
|
+
local link = env.newInstance(parent, "ObjectValue") :: ObjectValue
|
|
319
|
+
link.Name = "OtherLink"
|
|
320
|
+
link.Value = target
|
|
321
|
+
env.boot()
|
|
322
|
+
|
|
323
|
+
expect(#BinderUtils.getLinkedChildren(binder, "Link", parent)).toEqual(0)
|
|
324
|
+
|
|
325
|
+
env.destroy()
|
|
326
|
+
end)
|
|
327
|
+
|
|
328
|
+
it("deduplicates when two links point to the same bound target", function()
|
|
329
|
+
local env = setup()
|
|
330
|
+
|
|
331
|
+
local parent = env.newInstance()
|
|
332
|
+
local target = env.newInstance()
|
|
333
|
+
local binder = env.newBinder()
|
|
334
|
+
binder:Tag(target)
|
|
335
|
+
|
|
336
|
+
for _ = 1, 2 do
|
|
337
|
+
local link = env.newInstance(parent, "ObjectValue") :: ObjectValue
|
|
338
|
+
link.Name = "Link"
|
|
339
|
+
link.Value = target
|
|
340
|
+
end
|
|
341
|
+
env.boot()
|
|
342
|
+
|
|
343
|
+
expect(#BinderUtils.getLinkedChildren(binder, "Link", parent)).toEqual(1)
|
|
344
|
+
|
|
345
|
+
env.destroy()
|
|
346
|
+
end)
|
|
347
|
+
end)
|