return function()
local createSpy = require(script.Parent.createSpy)
local Type = require(script.Parent.Type)
local GlobalConfig = require(script.Parent.GlobalConfig)
local Binding = require(script.Parent.Binding)
describe("Binding.create", function()
it("should return a Binding object and an update function", function()
local binding, update = Binding.create(1)
expect(Type.of(binding)).to.equal(Type.Binding)
expect(typeof(update)).to.equal("function")
end)
it("should support tostring on bindings", function()
local binding, update = Binding.create(1)
expect(tostring(binding)).to.equal("RoactBinding(1)")
update("foo")
expect(tostring(binding)).to.equal("RoactBinding(foo)")
end)
end)
describe("Binding object", function()
it("should provide a getter and setter", function()
local binding, update = Binding.create(1)
expect(binding:getValue()).to.equal(1)
update(3)
expect(binding:getValue()).to.equal(3)
end)
it("should let users subscribe and unsubscribe to its updates", function()
local binding, update = Binding.create(1)
local spy = createSpy()
local disconnect = Binding.subscribe(binding, spy.value)
expect(spy.callCount).to.equal(0)
update(2)
expect(spy.callCount).to.equal(1)
spy:assertCalledWith(2)
disconnect()
update(3)
expect(spy.callCount).to.equal(1)
end)
end)
describe("Mapped bindings", function()
it("should be composable", function()
local word, updateWord = Binding.create("hi")
local wordLength = word:map(string.len)
local isEvenLength = wordLength:map(function(value)
return value % 2 == 0
end)
expect(word:getValue()).to.equal("hi")
expect(wordLength:getValue()).to.equal(2)
expect(isEvenLength:getValue()).to.equal(true)
updateWord("sup")
expect(word:getValue()).to.equal("sup")
expect(wordLength:getValue()).to.equal(3)
expect(isEvenLength:getValue()).to.equal(false)
end)
it("should cascade updates when subscribed", function()
local word, updateWord = Binding.create("hi")
local wordSpy = createSpy()
local disconnectWord = Binding.subscribe(word, wordSpy.value)
local length = word:map(string.len)
local lengthSpy = createSpy()
local disconnectLength = Binding.subscribe(length, lengthSpy.value)
local isEvenLength = length:map(function(value)
return value % 2 == 0
end)
local isEvenLengthSpy = createSpy()
local disconnectIsEvenLength = Binding.subscribe(isEvenLength, isEvenLengthSpy.value)
expect(wordSpy.callCount).to.equal(0)
expect(lengthSpy.callCount).to.equal(0)
expect(isEvenLengthSpy.callCount).to.equal(0)
updateWord("nice")
expect(wordSpy.callCount).to.equal(1)
wordSpy:assertCalledWith("nice")
expect(lengthSpy.callCount).to.equal(1)
lengthSpy:assertCalledWith(4)
expect(isEvenLengthSpy.callCount).to.equal(1)
isEvenLengthSpy:assertCalledWith(true)
disconnectWord()
disconnectLength()
disconnectIsEvenLength()
updateWord("goodbye")
expect(wordSpy.callCount).to.equal(1)
expect(isEvenLengthSpy.callCount).to.equal(1)
expect(lengthSpy.callCount).to.equal(1)
end)
it("should throw when updated directly", function()
local source = Binding.create(1)
local mapped = source:map(function(v)
return v
end)
expect(function()
Binding.update(mapped, 5)
end).to.throw()
end)
end)
describe("Binding.join", function()
it("should have getValue", function()
local binding1 = Binding.create(1)
local binding2 = Binding.create(2)
local binding3 = Binding.create(3)
local joinedBinding = Binding.join({
binding1,
binding2,
foo = binding3,
})
local bindingValue = joinedBinding:getValue()
expect(bindingValue).to.be.a("table")
expect(bindingValue[1]).to.equal(1)
expect(bindingValue[2]).to.equal(2)
expect(bindingValue.foo).to.equal(3)
end)
it("should update when any one of the subscribed bindings updates", function()
local binding1, update1 = Binding.create(1)
local binding2, update2 = Binding.create(2)
local binding3, update3 = Binding.create(3)
local joinedBinding = Binding.join({
binding1,
binding2,
foo = binding3,
})
local spy = createSpy()
Binding.subscribe(joinedBinding, spy.value)
expect(spy.callCount).to.equal(0)
update1(3)
expect(spy.callCount).to.equal(1)
local args = spy:captureValues("value")
expect(args.value).to.be.a("table")
expect(args.value[1]).to.equal(3)
expect(args.value[2]).to.equal(2)
expect(args.value["foo"]).to.equal(3)
update2(4)
expect(spy.callCount).to.equal(2)
args = spy:captureValues("value")
expect(args.value).to.be.a("table")
expect(args.value[1]).to.equal(3)
expect(args.value[2]).to.equal(4)
expect(args.value["foo"]).to.equal(3)
update3(8)
expect(spy.callCount).to.equal(3)
args = spy:captureValues("value")
expect(args.value).to.be.a("table")
expect(args.value[1]).to.equal(3)
expect(args.value[2]).to.equal(4)
expect(args.value["foo"]).to.equal(8)
end)
it("should disconnect from all upstream bindings", function()
local binding1, update1 = Binding.create(1)
local binding2, update2 = Binding.create(2)
local joined = Binding.join({ binding1, binding2 })
local spy = createSpy()
local disconnect = Binding.subscribe(joined, spy.value)
expect(spy.callCount).to.equal(0)
update1(3)
expect(spy.callCount).to.equal(1)
update2(3)
expect(spy.callCount).to.equal(2)
disconnect()
update1(4)
expect(spy.callCount).to.equal(2)
update2(2)
expect(spy.callCount).to.equal(2)
local value = joined:getValue()
expect(value[1]).to.equal(4)
expect(value[2]).to.equal(2)
end)
it("should be okay with calling disconnect multiple times", function()
local joined = Binding.join({})
local disconnect = Binding.subscribe(joined, function() end)
disconnect()
disconnect()
end)
it("should throw if updated directly", function()
local joined = Binding.join({})
expect(function()
Binding.update(joined, 0)
end)
end)
it("should throw when a non-table value is passed", function()
GlobalConfig.scoped({
typeChecks = true,
}, function()
expect(function()
Binding.join("hi")
end).to.throw()
end)
end)
it("should throw when a non-binding value is passed via table", function()
GlobalConfig.scoped({
typeChecks = true,
}, function()
expect(function()
local binding = Binding.create(123)
Binding.join({
binding,
"abcde",
})
end).to.throw()
end)
end)
end)
end