Skip to content
This repository has been archived by the owner on Dec 13, 2023. It is now read-only.

Commit

Permalink
Add snapshot serialization
Browse files Browse the repository at this point in the history
  • Loading branch information
jeparlefrancais committed Jul 19, 2019
1 parent 8450335 commit 5425c67
Show file tree
Hide file tree
Showing 15 changed files with 1,326 additions and 12 deletions.
3 changes: 3 additions & 0 deletions src/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ local GlobalConfig = require(script.GlobalConfig)
local createReconciler = require(script.createReconciler)
local createReconcilerCompat = require(script.createReconcilerCompat)
local RobloxRenderer = require(script.RobloxRenderer)
local shallow = require(script.shallow)
local strict = require(script.strict)
local Binding = require(script.Binding)

Expand Down Expand Up @@ -39,6 +40,8 @@ local Roact = strict {

setGlobalConfig = GlobalConfig.set,

shallow = shallow,

-- APIs that may change in the future without warning
UNSTABLE = {
},
Expand Down
1 change: 1 addition & 0 deletions src/init.spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ return function()
update = "function",
oneChild = "function",
setGlobalConfig = "function",
shallow = "function",

-- These functions are deprecated and throw warnings!
reify = "function",
Expand Down
60 changes: 48 additions & 12 deletions src/shallow.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ local Children = require(script.Parent.PropMarkers.Children)
local RobloxRenderer = require(script.Parent.RobloxRenderer)
local ElementKind = require(script.Parent.ElementKind)
local ElementUtils = require(script.Parent.ElementUtils)
local snapshot = require(script.Parent.snapshot)

local robloxReconciler = createReconciler(RobloxRenderer)

Expand Down Expand Up @@ -50,18 +51,19 @@ local function findNextVirtualNode(virtualNode, maxDepth)
end

local ContraintFunctions = {
kind = function(element, expectKind)
return ElementKind.of(element) == expectKind
kind = function(virtualNode, expectKind)
return ElementKind.of(virtualNode.currentElement) == expectKind
end,
className = function(element, className)
className = function(virtualNode, className)
local element = virtualNode.currentElement
local isHost = ElementKind.of(element) == ElementKind.Host
return isHost and element.component == className
end,
component = function(element, expectComponentValue)
return element.component == expectComponentValue
component = function(virtualNode, expectComponentValue)
return virtualNode.currentElement.component == expectComponentValue
end,
props = function(element, propSubSet)
local elementProps = element.props
props = function(virtualNode, propSubSet)
local elementProps = virtualNode.currentElement.props

for propKey, propValue in pairs(propSubSet) do
if elementProps[propKey] ~= propValue then
Expand All @@ -70,7 +72,10 @@ local ContraintFunctions = {
end

return true
end
end,
hostKey = function(virtualNode, expectHostKey)
return virtualNode.hostKey == expectHostKey
end,
}

local function countChildrenOfElement(element)
Expand Down Expand Up @@ -111,7 +116,7 @@ local function filterProps(props)

for key, value in pairs(props) do
if key ~= Children then
props[key] = value
filteredProps[key] = value
end
end

Expand All @@ -128,6 +133,8 @@ function ShallowWrapper.new(virtualNode, maxDepth)
_shallowChildren = nil,
type = getTypeFromVirtualNode(virtualNode),
props = filterProps(virtualNode.currentElement.props),
hostKey = virtualNode.hostKey,
instance = virtualNode.hostObject,
}

return setmetatable(wrapper, ShallowWrapperMetatable)
Expand All @@ -145,7 +152,7 @@ function ShallowWrapper:childrenCount()
end

function ShallowWrapper:find(constraints)
for constraint in pairs(constraints) do
for constraint in pairs(constraints) do
if not ContraintFunctions[constraint] then
error(('unknown constraint %q'):format(constraint))
end
Expand All @@ -164,6 +171,27 @@ function ShallowWrapper:find(constraints)
return results
end

function ShallowWrapper:findUnique(constraints)
local children = self:getChildren()

if constraints == nil then
assert(
#children == 1,
("expect to contain exactly one child, but found %d"):format(#children)
)
return children[1]
end

local constrainedChildren = self:find(constraints)

assert(
#constrainedChildren == 1,
("expect to find only one child, but found %d"):format(#constrainedChildren)
)

return constrainedChildren[1]
end

function ShallowWrapper:getChildren()
if self._shallowChildren then
return self._shallowChildren
Expand All @@ -179,13 +207,21 @@ function ShallowWrapper:getChildren()
return results
end

function ShallowWrapper:toMatchSnapshot(identifier)
assert(typeof(identifier) == "string", "Snapshot identifier must be a string")

local snapshotResult = snapshot(identifier, self)

snapshotResult:match()
end

function ShallowWrapper:_satisfiesAllContraints(constraints)
local element = self._virtualNode.currentElement
local virtualNode = self._virtualNode

for constraint, value in pairs(constraints) do
local constraintFunction = ContraintFunctions[constraint]

if not constraintFunction(element, value) then
if not constraintFunction(virtualNode, value) then
return false
end
end
Expand Down
131 changes: 131 additions & 0 deletions src/shallow.spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,37 @@ return function()
end)
end)

describe("instance", function()
it("should contain the instance when it is a host component", function()
local className = "Frame"
local function Component(props)
return createElement(className)
end

local element = createElement(Component)

local result = shallow(element)

expect(result.instance).to.be.ok()
expect(result.instance.ClassName).to.equal(className)
end)

it("should not have an instance if it is a function component", function()
local function Child()
return createElement("Frame")
end
local function Component(props)
return createElement(Child)
end

local element = createElement(Component)

local result = shallow(element)

expect(result.instance).never.to.be.ok()
end)
end)

describe("find children", function()
local function Component(props)
return createElement("Frame", {}, props.children)
Expand Down Expand Up @@ -648,6 +679,47 @@ return function()
end)
end)

describe("hostKey constraint", function()
it("should find the child element", function()
local hostKey = "Child"
local element = createElement(Component, {
children = {
[hostKey] = createElement("TextLabel"),
},
})

local result = shallow(element)

local constraints = {
hostKey = hostKey,
}
local children = result:find(constraints)

expect(#children).to.equal(1)

local child = children[1]

expect(child.type.kind).to.equal(ElementKind.Host)
end)

it("should return an empty list when no children is found", function()
local element = createElement(Component, {
children = {
Child = createElement("TextLabel"),
},
})

local result = shallow(element)

local constraints = {
hostKey = "NotFound",
}
local children = result:find(constraints)

expect(next(children)).never.to.be.ok()
end)
end)

it("should throw if the constraint does not exist", function()
local element = createElement("Frame")

Expand Down Expand Up @@ -734,4 +806,63 @@ return function()
expect(#children).to.equal(1)
end)
end)

describe("findUnique", function()
it("should return the only child when no constraints are given", function()
local element = createElement("Frame", {}, {
Child = createElement("TextLabel"),
})

local result = shallow(element)

local child = result:findUnique()

expect(child.type.kind).to.equal(ElementKind.Host)
expect(child.type.className).to.equal("TextLabel")
end)

it("should return the only child that satifies the constraint", function()
local element = createElement("Frame", {}, {
ChildA = createElement("TextLabel"),
ChildB = createElement("TextButton"),
})

local result = shallow(element)

local child = result:findUnique({
className = "TextLabel",
})

expect(child.type.className).to.equal("TextLabel")
end)

it("should throw if there is not any child element", function()
local element = createElement("Frame")

local result = shallow(element)

local function shouldThrow()
result:findUnique()
end

expect(shouldThrow).to.throw()
end)

it("should throw if more than one child satisfies the constraint", function()
local element = createElement("Frame", {}, {
ChildA = createElement("TextLabel"),
ChildB = createElement("TextLabel"),
})

local result = shallow(element)

local function shouldThrow()
result:findUnique({
className = "TextLabel",
})
end

expect(shouldThrow).to.throw()
end)
end)
end
5 changes: 5 additions & 0 deletions src/snapshot/Serialize/AnonymousFunction.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
local Symbol = require(script.Parent.Parent.Parent.Symbol)

local AnonymousFunction = Symbol.named("AnonymousFunction")

return AnonymousFunction
54 changes: 54 additions & 0 deletions src/snapshot/Serialize/IndentedOutput.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
local IndentedOutput = {}
local IndentedOutputMetatable = {
__index = IndentedOutput,
}

function IndentedOutput.new(indentation)
indentation = indentation or 2

local output = {
_level = 0,
_indentation = (" "):rep(indentation),
_lines = {},
}

setmetatable(output, IndentedOutputMetatable)

return output
end

function IndentedOutput:write(line, ...)
if select("#", ...) > 0 then
line = line:format(...)
end

local indentedLine = ("%s%s"):format(self._indentation:rep(self._level), line)

table.insert(self._lines, indentedLine)
end

function IndentedOutput:push()
self._level = self._level + 1
end

function IndentedOutput:pop()
self._level = math.max(self._level - 1, 0)
end

function IndentedOutput:writeAndPush(line)
self:write(line)
self:push()
end

function IndentedOutput:popAndWrite(line)
self:pop()
self:write(line)
end

function IndentedOutput:join(separator)
separator = separator or "\n"

return table.concat(self._lines, separator)
end

return IndentedOutput
Loading

0 comments on commit 5425c67

Please sign in to comment.