add gs
This commit is contained in:
@@ -0,0 +1,495 @@
|
||||
return function()
|
||||
local Roact = require(script.Parent)
|
||||
|
||||
it("should load with all public APIs", function()
|
||||
local publicApi = {
|
||||
createElement = "function",
|
||||
createRef = "function",
|
||||
mount = "function",
|
||||
unmount = "function",
|
||||
reconcile = "function",
|
||||
oneChild = "function",
|
||||
setGlobalConfig = "function",
|
||||
getGlobalConfigValue = "function",
|
||||
|
||||
-- These functions are deprecated and will throw warnings soon!
|
||||
reify = "function",
|
||||
teardown = "function",
|
||||
|
||||
Component = true,
|
||||
PureComponent = true,
|
||||
Portal = true,
|
||||
Children = true,
|
||||
Event = true,
|
||||
Change = true,
|
||||
Ref = true,
|
||||
None = true,
|
||||
Element = true,
|
||||
UNSTABLE = true,
|
||||
}
|
||||
|
||||
expect(Roact).to.be.ok()
|
||||
|
||||
for key, valueType in pairs(publicApi) do
|
||||
local success
|
||||
if typeof(valueType) == "string" then
|
||||
success = typeof(Roact[key]) == valueType
|
||||
else
|
||||
success = Roact[key] ~= nil
|
||||
end
|
||||
|
||||
if not success then
|
||||
local existence = typeof(valueType) == "boolean" and "present" or "of type " .. valueType
|
||||
local message = (
|
||||
"Expected public API member %q to be %s, but instead it was of type %s"
|
||||
):format(tostring(key), existence, typeof(Roact[key]))
|
||||
|
||||
error(message)
|
||||
end
|
||||
end
|
||||
|
||||
for key in pairs(Roact) do
|
||||
if publicApi[key] == nil then
|
||||
local message = (
|
||||
"Found unknown public API key %q!"
|
||||
):format(tostring(key))
|
||||
|
||||
error(message)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
describe("Props", function()
|
||||
it("should be passed to primitive components", function()
|
||||
local container = Instance.new("IntValue")
|
||||
|
||||
local element = Roact.createElement("StringValue", {
|
||||
Value = "foo",
|
||||
})
|
||||
|
||||
Roact.mount(element, container, "TestStringValue")
|
||||
|
||||
local rbx = container:FindFirstChild("TestStringValue")
|
||||
|
||||
expect(rbx).to.be.ok()
|
||||
expect(rbx.Value).to.equal("foo")
|
||||
end)
|
||||
|
||||
it("should be passed to functional components", function()
|
||||
local testProp = {}
|
||||
|
||||
local callCount = 0
|
||||
|
||||
local function TestComponent(props)
|
||||
expect(props.testProp).to.equal(testProp)
|
||||
callCount = callCount + 1
|
||||
end
|
||||
|
||||
local element = Roact.createElement(TestComponent, {
|
||||
testProp = testProp,
|
||||
})
|
||||
|
||||
Roact.mount(element)
|
||||
|
||||
-- The only guarantee is that the function will be invoked at least once
|
||||
expect(callCount > 0).to.equal(true)
|
||||
end)
|
||||
|
||||
it("should be passed to stateful components", function()
|
||||
local testProp = {}
|
||||
|
||||
local callCount = 0
|
||||
|
||||
local TestComponent = Roact.Component:extend("TestComponent")
|
||||
|
||||
function TestComponent:init(props)
|
||||
expect(props.testProp).to.equal(testProp)
|
||||
callCount = callCount + 1
|
||||
end
|
||||
|
||||
function TestComponent:render()
|
||||
end
|
||||
|
||||
local element = Roact.createElement(TestComponent, {
|
||||
testProp = testProp,
|
||||
})
|
||||
|
||||
Roact.mount(element)
|
||||
|
||||
expect(callCount).to.equal(1)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("State", function()
|
||||
it("should trigger a re-render of child components", function()
|
||||
local renderCount = 0
|
||||
local listener = nil
|
||||
|
||||
local TestChild = Roact.Component:extend("TestChild")
|
||||
|
||||
function TestChild:render()
|
||||
renderCount = renderCount + 1
|
||||
return nil
|
||||
end
|
||||
|
||||
local TestParent = Roact.Component:extend("TestParent")
|
||||
|
||||
function TestParent:init(props)
|
||||
self.state = {
|
||||
value = 0,
|
||||
}
|
||||
end
|
||||
|
||||
function TestParent:didMount()
|
||||
listener = function()
|
||||
self:setState({
|
||||
value = self.state.value + 1,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
function TestParent:render()
|
||||
return Roact.createElement(TestChild, {
|
||||
value = self.state.value,
|
||||
})
|
||||
end
|
||||
|
||||
local element = Roact.createElement(TestParent)
|
||||
Roact.mount(element)
|
||||
|
||||
expect(renderCount >= 1).to.equal(true)
|
||||
expect(listener).to.be.a("function")
|
||||
|
||||
listener()
|
||||
|
||||
expect(renderCount >= 2).to.equal(true)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("Context", function()
|
||||
it("should be passed to children through primitive and functional components", function()
|
||||
local testValue = {}
|
||||
|
||||
local callCount = 0
|
||||
|
||||
local ContextConsumer = Roact.Component:extend("ContextConsumer")
|
||||
|
||||
function ContextConsumer:init(props)
|
||||
expect(self._context.testValue).to.equal(testValue)
|
||||
|
||||
callCount = callCount + 1
|
||||
end
|
||||
|
||||
function ContextConsumer:render()
|
||||
return
|
||||
end
|
||||
|
||||
local function ContextBarrier(props)
|
||||
return Roact.createElement(ContextConsumer)
|
||||
end
|
||||
|
||||
local ContextProvider = Roact.Component:extend("ContextProvider")
|
||||
|
||||
function ContextProvider:init(props)
|
||||
self._context.testValue = props.testValue
|
||||
end
|
||||
|
||||
function ContextProvider:render()
|
||||
return Roact.createElement("Frame", {}, {
|
||||
Child = Roact.createElement(ContextBarrier),
|
||||
})
|
||||
end
|
||||
|
||||
local element = Roact.createElement(ContextProvider, {
|
||||
testValue = testValue,
|
||||
})
|
||||
|
||||
Roact.mount(element)
|
||||
|
||||
expect(callCount).to.equal(1)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("Ref", function()
|
||||
it("should call back with a Roblox object after properties and children", function()
|
||||
local callCount = 0
|
||||
|
||||
local function ref(rbx)
|
||||
expect(rbx).to.be.ok()
|
||||
expect(rbx.ClassName).to.equal("StringValue")
|
||||
expect(rbx.Value).to.equal("Hey!")
|
||||
expect(rbx.Name).to.equal("RefTest")
|
||||
expect(#rbx:GetChildren()).to.equal(1)
|
||||
|
||||
callCount = callCount + 1
|
||||
end
|
||||
|
||||
local element = Roact.createElement("StringValue", {
|
||||
Value = "Hey!",
|
||||
[Roact.Ref] = ref,
|
||||
}, {
|
||||
TestChild = Roact.createElement("StringValue"),
|
||||
})
|
||||
|
||||
Roact.mount(element, nil, "RefTest")
|
||||
|
||||
expect(callCount).to.equal(1)
|
||||
end)
|
||||
|
||||
it("should pass nil to refs for tearing down", function()
|
||||
local callCount = 0
|
||||
local currentRef
|
||||
|
||||
local function ref(rbx)
|
||||
currentRef = rbx
|
||||
callCount = callCount + 1
|
||||
end
|
||||
|
||||
local element = Roact.createElement("StringValue", {
|
||||
[Roact.Ref] = ref,
|
||||
})
|
||||
|
||||
local instance = Roact.mount(element, nil, "RefTest")
|
||||
|
||||
expect(callCount).to.equal(1)
|
||||
expect(currentRef).to.be.ok()
|
||||
expect(currentRef.Name).to.equal("RefTest")
|
||||
|
||||
Roact.unmount(instance)
|
||||
|
||||
expect(callCount).to.equal(2)
|
||||
expect(currentRef).to.equal(nil)
|
||||
end)
|
||||
|
||||
it("should tear down refs when switched out of the tree", function()
|
||||
local updateMethod
|
||||
local refCount = 0
|
||||
local currentRef
|
||||
|
||||
local function ref(rbx)
|
||||
currentRef = rbx
|
||||
refCount = refCount + 1
|
||||
end
|
||||
|
||||
local function RefWrapper()
|
||||
return Roact.createElement("StringValue", {
|
||||
Value = "ooba ooba",
|
||||
[Roact.Ref] = ref,
|
||||
})
|
||||
end
|
||||
|
||||
local Root = Roact.Component:extend("RefTestRoot")
|
||||
|
||||
function Root:init()
|
||||
updateMethod = function(show)
|
||||
self:setState({
|
||||
show = show,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
function Root:render()
|
||||
if self.state.show then
|
||||
return Roact.createElement(RefWrapper)
|
||||
end
|
||||
end
|
||||
|
||||
local element = Roact.createElement(Root)
|
||||
Roact.mount(element)
|
||||
|
||||
expect(refCount).to.equal(0)
|
||||
expect(currentRef).to.equal(nil)
|
||||
|
||||
updateMethod(true)
|
||||
|
||||
expect(refCount).to.equal(1)
|
||||
expect(currentRef.Value).to.equal("ooba ooba")
|
||||
|
||||
updateMethod(false)
|
||||
|
||||
expect(refCount).to.equal(2)
|
||||
expect(currentRef).to.equal(nil)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("Portal", function()
|
||||
it("should place all children as children of the target Roblox instance", function()
|
||||
local target = Instance.new("Folder")
|
||||
|
||||
local function FunctionalComponent(props)
|
||||
local intValue = props.value
|
||||
|
||||
return Roact.createElement("IntValue", {
|
||||
Value = intValue,
|
||||
})
|
||||
end
|
||||
|
||||
local portal = Roact.createElement(Roact.Portal, {
|
||||
target = target
|
||||
}, {
|
||||
folderOne = Roact.createElement("Folder"),
|
||||
folderTwo = Roact.createElement("Folder"),
|
||||
intValueOne = Roact.createElement(FunctionalComponent, {
|
||||
value = 42,
|
||||
}),
|
||||
})
|
||||
Roact.mount(portal)
|
||||
|
||||
expect(target:FindFirstChild("folderOne")).to.be.ok()
|
||||
expect(target:FindFirstChild("folderTwo")).to.be.ok()
|
||||
expect(target:FindFirstChild("intValueOne")).to.be.ok()
|
||||
expect(target:FindFirstChild("intValueOne").Value).to.equal(42)
|
||||
end)
|
||||
|
||||
it("should error if the target is nil", function()
|
||||
local portal = Roact.createElement(Roact.Portal, {}, {
|
||||
folderOne = Roact.createElement("Folder"),
|
||||
folderTwo = Roact.createElement("Folder"),
|
||||
})
|
||||
|
||||
expect(function()
|
||||
Roact.mount(portal)
|
||||
end).to.throw()
|
||||
end)
|
||||
|
||||
it("should error if the target is not a Roblox instance", function()
|
||||
local portal = Roact.createElement(Roact.Portal, {
|
||||
target = "NotARobloxInstance",
|
||||
}, {
|
||||
folderOne = Roact.createElement("Folder"),
|
||||
folderTwo = Roact.createElement("Folder"),
|
||||
})
|
||||
|
||||
expect(function()
|
||||
Roact.mount(portal)
|
||||
end).to.throw()
|
||||
end)
|
||||
|
||||
it("should update if parent changes the target", function()
|
||||
local targetOne = Instance.new("Folder")
|
||||
local targetTwo = Instance.new("Folder")
|
||||
local countWillUnmount = 0
|
||||
local changeState
|
||||
|
||||
local TestUnmountComponent = Roact.Component:extend("TestUnmountComponent")
|
||||
|
||||
function TestUnmountComponent:render()
|
||||
return nil
|
||||
end
|
||||
|
||||
function TestUnmountComponent:willUnmount()
|
||||
countWillUnmount = countWillUnmount + 1
|
||||
end
|
||||
|
||||
local PortalContainer = Roact.Component:extend("PortalContainer")
|
||||
|
||||
function PortalContainer:init()
|
||||
self.state = {
|
||||
target = targetOne,
|
||||
}
|
||||
end
|
||||
|
||||
function PortalContainer:render()
|
||||
return Roact.createElement(Roact.Portal, {
|
||||
target = self.state.target,
|
||||
}, {
|
||||
folderOne = Roact.createElement("Folder"),
|
||||
folderTwo = Roact.createElement("Folder"),
|
||||
testUnmount = Roact.createElement(TestUnmountComponent),
|
||||
})
|
||||
end
|
||||
|
||||
function PortalContainer:didMount()
|
||||
expect(self.state.target:FindFirstChild("folderOne")).to.be.ok()
|
||||
expect(self.state.target:FindFirstChild("folderTwo")).to.be.ok()
|
||||
|
||||
changeState = function(newState)
|
||||
self:setState(newState)
|
||||
end
|
||||
end
|
||||
|
||||
Roact.mount(Roact.createElement(PortalContainer))
|
||||
|
||||
expect(targetOne:FindFirstChild("folderOne")).to.be.ok()
|
||||
expect(targetOne:FindFirstChild("folderTwo")).to.be.ok()
|
||||
|
||||
changeState({
|
||||
target = targetTwo,
|
||||
})
|
||||
|
||||
expect(countWillUnmount).to.equal(1)
|
||||
|
||||
expect(targetOne:FindFirstChild("folderOne")).never.to.be.ok()
|
||||
expect(targetOne:FindFirstChild("folderTwo")).never.to.be.ok()
|
||||
expect(targetTwo:FindFirstChild("folderOne")).to.be.ok()
|
||||
expect(targetTwo:FindFirstChild("folderTwo")).to.be.ok()
|
||||
end)
|
||||
|
||||
it("should update Roblox instance properties when relevant parent props are changed", function()
|
||||
local target = Instance.new("Folder")
|
||||
local changeState
|
||||
|
||||
local PortalContainer = Roact.Component:extend("PortalContainer")
|
||||
|
||||
function PortalContainer:init()
|
||||
self.state = {
|
||||
value = "initialStringValue",
|
||||
}
|
||||
end
|
||||
|
||||
function PortalContainer:render()
|
||||
return Roact.createElement(Roact.Portal, {
|
||||
target = target,
|
||||
}, {
|
||||
TestStringValue = Roact.createElement("StringValue", {
|
||||
Value = self.state.value,
|
||||
})
|
||||
})
|
||||
end
|
||||
|
||||
function PortalContainer:didMount()
|
||||
changeState = function(newState)
|
||||
self:setState(newState)
|
||||
end
|
||||
end
|
||||
|
||||
Roact.mount(Roact.createElement(PortalContainer))
|
||||
|
||||
expect(target:FindFirstChild("TestStringValue")).to.be.ok()
|
||||
expect(target:FindFirstChild("TestStringValue").Value).to.equal("initialStringValue")
|
||||
|
||||
changeState({
|
||||
value = "newStringValue",
|
||||
})
|
||||
|
||||
expect(target:FindFirstChild("TestStringValue")).to.be.ok()
|
||||
expect(target:FindFirstChild("TestStringValue").Value).to.equal("newStringValue")
|
||||
end)
|
||||
|
||||
it("should properly teardown the Portal", function()
|
||||
local target = Instance.new("Folder")
|
||||
|
||||
local portal = Roact.createElement(Roact.Portal, {
|
||||
target = target
|
||||
}, {
|
||||
folderOne = Roact.createElement("Folder"),
|
||||
folderTwo = Roact.createElement("Folder"),
|
||||
})
|
||||
local instance = Roact.mount(portal)
|
||||
|
||||
local folderThree = Instance.new("Folder")
|
||||
folderThree.Name = "folderThree"
|
||||
folderThree.Parent = target
|
||||
|
||||
expect(target:FindFirstChild("folderOne")).to.be.ok()
|
||||
expect(target:FindFirstChild("folderTwo")).to.be.ok()
|
||||
expect(target:FindFirstChild("folderThree")).to.be.ok()
|
||||
|
||||
Roact.unmount(instance)
|
||||
|
||||
expect(target:FindFirstChild("folderOne")).never.to.be.ok()
|
||||
expect(target:FindFirstChild("folderTwo")).never.to.be.ok()
|
||||
expect(target:FindFirstChild("folderThree")).to.be.ok()
|
||||
end)
|
||||
end)
|
||||
end
|
||||
Reference in New Issue
Block a user