local j=require("\064\112\107\103\047\064\106\115\100\111\116\108\117\097\047\106\101\115\116-\103\108\111\098\097\108\115")local d=j.beforeEach local A=j.expect local f=j.describe local T=j.it local Z local O local I d(function()Z=require("\064\112\107\103\047\064\106\115\100\111\116\108\117\097\047\114\101\097\099\116")O=require("\046\046")I=O.createRenderer end)local function g(j)if _G.__DEV__ then j._store.validated=true end return j end f("\082\101\097\099\116\083\104\097\108\108\111\119\082\101\110\100\101\114\101\114 \119\105\116\104 \104\111\111\107\115",function()T("\115\104\111\117\108\100 \119\111\114\107 \119\105\116\104 \117\115\101\083\116\097\116\101",function()local function j(j)local d=Z.useState(j.defaultName)return Z.createElement("\084\101\120\116\076\097\098\101\108",{Text="\089\111\117\114 \110\097\109\101 \105\115\058 "..d})end local d=I()local f=d:render(Z.createElement(j,{defaultName="\068\111\109\105\110\105\099"}));(A(f)).toEqual(Z.createElement("\084\101\120\116\076\097\098\101\108",{Text="\089\111\117\114 \110\097\109\101 \105\115\058 \068\111\109\105\110\105\099"}))f=d:render(Z.createElement(j,{defaultName="\083\104\111\117\108\100 \110\111\116 \117\115\101 \116\104\105\115 \110\097\109\101"}));(A(f)).toEqual(Z.createElement("\084\101\120\116\076\097\098\101\108",{Text="\089\111\117\114 \110\097\109\101 \105\115\058 \068\111\109\105\110\105\099"}))end)T("\115\104\111\117\108\100 \119\111\114\107 \119\105\116\104 \117\112\100\097\116\105\110\103 \097 \118\097\108\117\101 \102\114\111\109 \117\115\101\083\116\097\116\101",function()local function j(j)local d,A=Z.useState(j.defaultName)if d~="\068\097\110"then A("\068\097\110")end return Z.createElement("\070\114\097\109\101",nil,Z.createElement("\084\101\120\116\076\097\098\101\108",{Text="\089\111\117\114 \110\097\109\101 \105\115\058 "..d}))end local d=I()local f=d:render(Z.createElement(j,{defaultName="\068\111\109\105\110\105\099"}));(A(f)).toEqual(Z.createElement("\070\114\097\109\101",nil,Z.createElement("\084\101\120\116\076\097\098\101\108",{Text="\089\111\117\114 \110\097\109\101 \105\115\058 \068\097\110"})))end)T("\115\104\111\117\108\100 \119\111\114\107 \119\105\116\104 \117\112\100\097\116\105\110\103 \097 \100\101\114\105\118\101\100 \118\097\108\117\101 \102\114\111\109 \117\115\101\083\116\097\116\101",function()local j local function d(d)local A,f=Z.useState(d.defaultName)local T,O=Z.useState(d.defaultName)local I,g=Z.useState(string.sub(A,1,1))j=f if A~=T then O(A)g(string.sub(A,1,1))end return Z.createElement("\070\114\097\109\101",nil,Z.createElement("\084\101\120\116\076\097\098\101\108",{Text="\089\111\117\114 \110\097\109\101 \105\115\058 "..(A..(" \040"..(tostring(I).."\041")))}))end local f=I()local T=f:render(Z.createElement(d,{defaultName="\083\111\112\104\105\101"}));(A(T)).toEqual(Z.createElement("\070\114\097\109\101",nil,Z.createElement("\084\101\120\116\076\097\098\101\108",{Text="\089\111\117\114 \110\097\109\101 \105\115\058 \083\111\112\104\105\101 \040\083\041"})))T=f:render(Z.createElement(d,{defaultName="\068\097\110"}));(A(T)).toEqual(Z.createElement("\070\114\097\109\101",nil,Z.createElement("\084\101\120\116\076\097\098\101\108",{Text="\089\111\117\114 \110\097\109\101 \105\115\058 \083\111\112\104\105\101 \040\083\041"})))j("\068\097\110");(A(f:getRenderOutput())).toEqual(Z.createElement("\070\114\097\109\101",nil,Z.createElement("\084\101\120\116\076\097\098\101\108",{Text="\089\111\117\114 \110\097\109\101 \105\115\058 \068\097\110 \040\068\041"})))end)T("\115\104\111\117\108\100 \119\111\114\107 \119\105\116\104 \117\115\101\082\101\100\117\099\101\114",function()local function j(j,d)if d.type=="\105\110\099\114\101\109\101\110\116"then return{count=j.count+1}elseif d.type=="\100\101\099\114\101\109\101\110\116"then return{count=j.count-1}else error("\105\109\112\111\115\115\105\098\108\101")end end local function d(d)local A=Z.useReducer(j,d,function(j)return{count=j.initialCount}end)return Z.createElement("\070\114\097\109\101",nil,Z.createElement("\084\101\120\116\076\097\098\101\108",{"\084\104\101 \099\111\117\110\116\101\114 \105\115 \097\116\058 "..tostring(A.count)}))end local f=I()local T=f:render(Z.createElement(d,{initialCount=0}));(A(T)).toEqual(Z.createElement("\070\114\097\109\101",nil,Z.createElement("\084\101\120\116\076\097\098\101\108",{"\084\104\101 \099\111\117\110\116\101\114 \105\115 \097\116\058 \048"})))T=f:render(Z.createElement(d,{initialCount=10}));(A(T)).toEqual(Z.createElement("\070\114\097\109\101",nil,Z.createElement("\084\101\120\116\076\097\098\101\108",{"\084\104\101 \099\111\117\110\116\101\114 \105\115 \097\116\058 \048"})))end)T("\115\104\111\117\108\100 \119\111\114\107 \119\105\116\104 \097 \100\105\115\112\097\116\099\104\101\100 \115\116\097\116\101 \099\104\097\110\103\101 \102\111\114 \097 \117\115\101\082\101\100\117\099\101\114",function()local function j(j,d)if d.type=="\105\110\099\114\101\109\101\110\116"then return{count=j.count+1}elseif d.type=="\100\101\099\114\101\109\101\110\116"then return{count=j.count-1}else error("\105\109\112\111\115\115\105\098\108\101")end end local function d(d)local A,f=Z.useReducer(j,d,function(j)return{count=j.initialCount}end)if A.count==0 then f({type="\105\110\099\114\101\109\101\110\116"})end return Z.createElement("\070\114\097\109\101",nil,Z.createElement("\084\101\120\116\076\097\098\101\108",{"\084\104\101 \099\111\117\110\116\101\114 \105\115 \097\116\058 "..tostring(A.count)}))end local f=I()local T=f:render(Z.createElement(d,{initialCount=0}));(A(T)).toEqual(Z.createElement("\070\114\097\109\101",nil,Z.createElement("\084\101\120\116\076\097\098\101\108",{"\084\104\101 \099\111\117\110\116\101\114 \105\115 \097\116\058 \049"})))end)T("\115\104\111\117\108\100 \110\111\116 \116\114\105\103\103\101\114 \101\102\102\101\099\116\115",function()local j={}local function d(d)Z.useEffect(function()table.insert(j,"\117\115\101\069\102\102\101\099\116")end)Z.useLayoutEffect(function()table.insert(j,"\117\115\101\069\102\102\101\099\116")end)return Z.createElement("\084\101\120\116",nil,"\072\101\108\108\111 \119\111\114\108\100")end local f=I()f:render(Z.createElement(d));(A(j)).toEqual({})end)T("\115\104\111\117\108\100 \119\111\114\107 \119\105\116\104 \117\115\101\082\101\102",function()local function j()local j=Z.useRef({number=math.random()})return Z.createElement("\070\114\097\109\101",nil,Z.createElement("\084\101\120\116\076\097\098\101\108",{Text="\084\104\101 \114\097\110\100\111\109 \110\117\109\098\101\114 \105\115\058 "..tostring(j.current.number)}))end local d=I()local f=d:render(Z.createElement(j))local T=d:render(Z.createElement(j));(A(f)).toEqual(T)end)T("\115\104\111\117\108\100 \119\111\114\107 \119\105\116\104 \117\115\101\077\101\109\111",function()local function j()local j=Z.useMemo(function()return{number=math.random()}end,{})return Z.createElement("\070\114\097\109\101",nil,Z.createElement("\084\101\120\116\076\097\098\101\108",{Text="\084\104\101 \114\097\110\100\111\109 \110\117\109\098\101\114 \105\115\058 "..tostring(j.number)}))end local d=I()local f=d:render(Z.createElement(j))local T=d:render(Z.createElement(j));(A(f)).toEqual(T)end)T("\115\104\111\117\108\100 \119\111\114\107 \119\105\116\104 \117\115\101\077\101\109\111 \119\105\116\104 \109\117\108\116\105\112\108\101 \114\101\116\117\114\110 \118\097\108\117\101\115",function()local j,d local function f()local A,f=Z.useMemo(function()return math.random(),math.random()end,{})j,d=A,f return Z.createElement("\070\114\097\109\101",nil,Z.createElement("\084\101\120\116\076\097\098\101\108",{Text=tostring(A)..(" "..tostring(f))}))end local T=I()local O=T:render(Z.createElement(f))local g=T:render(Z.createElement(f));(A(O)).toEqual(g);(A(j)).toBeDefined();(A(d)).toBeDefined()end)T("\115\104\111\117\108\100 \119\111\114\107 \119\105\116\104 \117\115\101\067\111\110\116\101\120\116",function()local j=Z.createContext("\100\101\102\097\117\108\116")local function d()local d=Z.useContext(j)return Z.createElement("\070\114\097\109\101",nil,Z.createElement("\084\101\120\116\076\097\098\101\108",{Text=tostring(d)}))end local f=I()local T=f:render(Z.createElement(d));(A(T)).toEqual(Z.createElement("\070\114\097\109\101",nil,Z.createElement("\084\101\120\116\076\097\098\101\108",{Text="\100\101\102\097\117\108\116"})))end)T("\115\104\111\117\108\100 \110\111\116 \108\101\097\107 \115\116\097\116\101 \119\104\101\110 \099\111\109\112\111\110\101\110\116 \116\121\112\101 \099\104\097\110\103\101\115",function()local function j(j)local d=Z.useState(j.defaultName)return Z.createElement("\084\101\120\116\076\097\098\101\108",{Text="\089\111\117\114 \110\097\109\101 \105\115\058 "..d})end local function d(j)local d=Z.useState(j.defaultName)return Z.createElement("\084\101\120\116\076\097\098\101\108",{Text="\089\111\117\114 \110\097\109\101 \105\115\058 "..d})end local f=I()local T=f:render(Z.createElement(j,{defaultName="\068\111\109\105\110\105\099"}));(A(T)).toEqual(Z.createElement("\084\101\120\116\076\097\098\101\108",{Text="\089\111\117\114 \110\097\109\101 \105\115\058 \068\111\109\105\110\105\099"}))T=f:render(Z.createElement(d,{defaultName="\068\097\110"}));(A(T)).toEqual(Z.createElement("\084\101\120\116\076\097\098\101\108",{Text="\089\111\117\114 \110\097\109\101 \105\115\058 \068\097\110"}))end)T("\115\104\111\117\108\100 \119\111\114\107 \119\105\116\104 \119\105\116\104 \102\111\114\119\097\114\100\082\101\102 \043 \097\110\121 \104\111\111\107",function()local j=Z.forwardRef(function(j,d)local A=Z.useRef({number=math.random()})return Z.createElement("\070\114\097\109\101",{ref=d},Z.createElement("\084\101\120\116\076\097\098\101\108",{Text="\084\104\101 \114\097\110\100\111\109 \110\117\109\098\101\114 \105\115\058 "..tostring(A.current.number)}))end)local d=I()local f=d:render(Z.createElement(j))local T=d:render(Z.createElement(j));(A(f)).toEqual(T)end)T("\115\104\111\117\108\100 \117\112\100\097\116\101 \097 \118\097\108\117\101 \102\114\111\109 \117\115\101\083\116\097\116\101 \111\117\116\115\105\100\101 \116\104\101 \114\101\110\100\101\114",function()local j local function d(d)local A,f=Z.useReducer(function(j,d)if d=="\105\110\099"then return j+1 end return j end,0)local T,O=Z.useState(d.defaultName)j=function(...)return f("\105\110\099")end return Z.createElement("\070\114\097\109\101",{onClick=function()O("\068\097\110")end},Z.createElement("\084\101\120\116\076\097\098\101\108",{Text="\089\111\117\114 \110\097\109\101 \105\115\058 "..(T..(" \040"..(A.."\041")))}))end local f=I()local T=Z.createElement(d,{defaultName="\068\111\109\105\110\105\099"})local O=f:render(T);(A(O.props.children)).toEqual(g(Z.createElement("\084\101\120\116\076\097\098\101\108",{Text="\089\111\117\114 \110\097\109\101 \105\115\058 \068\111\109\105\110\105\099 \040\048\041"})))O.props.onClick()local G=f:render(T);(A(G.props.children)).toEqual(g(Z.createElement("\084\101\120\116\076\097\098\101\108",{Text="\089\111\117\114 \110\097\109\101 \105\115\058 \068\097\110 \040\048\041"})))j("\102\111\111")G=f:render(T);(A(G.props.children)).toEqual(g(Z.createElement("\084\101\120\116\076\097\098\101\108",{Text="\089\111\117\114 \110\097\109\101 \105\115\058 \068\097\110 \040\049\041"})))j("\105\110\099")G=f:render(T);(A(G.props.children)).toEqual(g(Z.createElement("\084\101\120\116\076\097\098\101\108",{Text="\089\111\117\114 \110\097\109\101 \105\115\058 \068\097\110 \040\050\041"})))end)T("\115\104\111\117\108\100 \105\103\110\111\114\101 \097 \102\111\114\101\105\103\110 \117\112\100\097\116\101 \111\117\116\115\105\100\101 \116\104\101 \114\101\110\100\101\114",function()local j local function d()local d,A=Z.useState(0)if not j then j=A end return d end local f=I()local T=Z.createElement(d)local O=f:render(T);(A(O)).toEqual(0)j(1)O=f:render(T);(A(O)).toEqual(1)f:unmount()O=f:render(T);(A(O)).toEqual(0)j(1)O=f:render(T);(A(O)).toEqual(0)end)T("\115\104\111\117\108\100 \110\111\116 \102\111\114\103\101\116 \114\101\110\100\101\114 \112\104\097\115\101 \117\112\100\097\116\101\115",function()local j local function d()local d,A=Z.useState(0)j=A if d<5 then A(function(j)return j+1 end)end return d end local f=I()local T=Z.createElement(d)local O=f:render(T);(A(O)).toEqual(5)j(10)O=f:render(T);(A(O)).toEqual(10)j(function(j)return j+1 end)O=f:render(T);(A(O)).toEqual(11)j(function(j)return j-10 end)O=f:render(T);(A(O)).toEqual(5)end)end)
--[[*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @emails react-core
 * @jest-environment node
]]

local JestGlobals = require("@pkg/@jsdotlua/jest-globals")
local beforeEach = JestGlobals.beforeEach
local jestExpect = JestGlobals.expect
local describe = JestGlobals.describe
local it = JestGlobals.it
local React
local ReactShallowRenderer

local createRenderer

beforeEach(function()
	React = require("@pkg/@jsdotlua/react")
	ReactShallowRenderer = require("..")
	createRenderer = ReactShallowRenderer.createRenderer
end)

local function validateElement(element)
	if _G.__DEV__ then
		element._store.validated = true
	end
	return element
end

describe("ReactShallowRenderer with hooks", function()
	it("should work with useState", function()
		local function SomeComponent(props)
			local name = React.useState(props.defaultName)

			return React.createElement("TextLabel", {
				Text = "Your name is: " .. name,
			})
		end

		local shallowRenderer = createRenderer()
		local result = shallowRenderer:render(React.createElement(SomeComponent, {
			defaultName = "Dominic",
		}))

		jestExpect(result).toEqual(React.createElement("TextLabel", {
			Text = "Your name is: Dominic",
		}))

		result = shallowRenderer:render(React.createElement(SomeComponent, {
			defaultName = "Should not use this name",
		}))

		jestExpect(result).toEqual(React.createElement("TextLabel", {
			Text = "Your name is: Dominic",
		}))
	end)

	it("should work with updating a value from useState", function()
		local function SomeComponent(props)
			local name, updateName = React.useState(props.defaultName)

			if name ~= "Dan" then
				updateName("Dan")
			end

			return React.createElement(
				"Frame",
				nil,
				React.createElement("TextLabel", {
					Text = "Your name is: " .. name,
				})
			)
		end

		local shallowRenderer = createRenderer()
		local result = shallowRenderer:render(
			React.createElement(SomeComponent, { defaultName = "Dominic" })
		)

		jestExpect(result).toEqual(
			React.createElement(
				"Frame",
				nil,
				React.createElement("TextLabel", {
					Text = "Your name is: " .. "Dan",
				})
			)
		)
	end)

	it("should work with updating a derived value from useState", function()
		local _updateName

		local function SomeComponent(props)
			local name, updateName = React.useState(props.defaultName)
			local prevName, updatePrevName = React.useState(props.defaultName)
			local letter, updateLetter = React.useState(string.sub(name, 1, 1))

			_updateName = updateName

			if name ~= prevName then
				updatePrevName(name)
				updateLetter(string.sub(name, 1, 1))
			end

			return React.createElement(
				"Frame",
				nil,
				React.createElement("TextLabel", {
					Text = "Your name is: " .. name .. " (" .. tostring(letter) .. ")",
				})
			)
		end

		local shallowRenderer = createRenderer()
		local result = shallowRenderer:render(
			React.createElement(SomeComponent, { defaultName = "Sophie" })
		)

		jestExpect(result).toEqual(
			React.createElement(
				"Frame",
				nil,
				React.createElement("TextLabel", {
					Text = "Your name is: " .. "Sophie (S)",
				})
			)
		)

		result = shallowRenderer:render(
			React.createElement(SomeComponent, { defaultName = "Dan" })
		)
		jestExpect(result).toEqual(
			React.createElement(
				"Frame",
				nil,
				React.createElement("TextLabel", {
					Text = "Your name is: " .. "Sophie (S)",
				})
			)
		)

		_updateName("Dan")
		jestExpect(shallowRenderer:getRenderOutput()).toEqual(
			React.createElement(
				"Frame",
				nil,
				React.createElement("TextLabel", {
					Text = "Your name is: " .. "Dan (D)",
				})
			)
		)
	end)

	it("should work with useReducer", function()
		local function reducer(state, action)
			if action.type == "increment" then
				return { count = state.count + 1 }
			elseif action.type == "decrement" then
				return { count = state.count - 1 }
			else
				error("impossible")
			end
		end

		local function SomeComponent(props)
			local state = React.useReducer(reducer, props, function(p)
				return {
					count = p.initialCount,
				}
			end)

			return React.createElement(
				"Frame",
				nil,
				React.createElement("TextLabel", {
					"The counter is at: " .. tostring(state.count),
				})
			)
		end

		local shallowRenderer = createRenderer()
		local result = shallowRenderer:render(
			React.createElement(SomeComponent, { initialCount = 0 })
		)
		jestExpect(result).toEqual(
			React.createElement(
				"Frame",
				nil,
				React.createElement("TextLabel", {
					"The counter is at: 0",
				})
			)
		)

		result = shallowRenderer:render(
			React.createElement(SomeComponent, { initialCount = 10 })
		)

		jestExpect(result).toEqual(
			React.createElement(
				"Frame",
				nil,
				React.createElement("TextLabel", {
					"The counter is at: 0",
				})
			)
		)
	end)

	it("should work with a dispatched state change for a useReducer", function()
		local function reducer(state, action)
			if action.type == "increment" then
				return { count = state.count + 1 }
			elseif action.type == "decrement" then
				return { count = state.count - 1 }
			else
				error("impossible")
			end
		end

		local function SomeComponent(props)
			local state, dispatch = React.useReducer(reducer, props, function(p)
				return {
					count = p.initialCount,
				}
			end)

			if state.count == 0 then
				dispatch({ type = "increment" })
			end
			return React.createElement(
				"Frame",
				nil,
				React.createElement("TextLabel", {
					"The counter is at: " .. tostring(state.count),
				})
			)
		end

		local shallowRenderer = createRenderer()
		local result = shallowRenderer:render(
			React.createElement(SomeComponent, { initialCount = 0 })
		)

		jestExpect(result).toEqual(
			React.createElement(
				"Frame",
				nil,
				React.createElement("TextLabel", {
					"The counter is at: 1",
				})
			)
		)
	end)

	it("should not trigger effects", function()
		local effectsCalled = {}

		local function SomeComponent(props)
			React.useEffect(function()
				table.insert(effectsCalled, "useEffect")
			end)

			React.useLayoutEffect(function()
				table.insert(effectsCalled, "useEffect")
			end)

			return React.createElement("Text", nil, "Hello world")
		end

		local shallowRenderer = createRenderer()
		shallowRenderer:render(React.createElement(SomeComponent))

		jestExpect(effectsCalled).toEqual({})
	end)

	it("should work with useRef", function()
		local function SomeComponent()
			local randomNumberRef = React.useRef({ number = math.random() })

			return React.createElement(
				"Frame",
				nil,
				React.createElement("TextLabel", {
					Text = "The random number is: "
						.. tostring(randomNumberRef.current.number),
				})
			)
		end

		local shallowRenderer = createRenderer()
		local firstResult = shallowRenderer:render(React.createElement(SomeComponent))
		local secondResult = shallowRenderer:render(React.createElement(SomeComponent))

		jestExpect(firstResult).toEqual(secondResult)
	end)

	it("should work with useMemo", function()
		local function SomeComponent()
			local randomNumber = React.useMemo(function()
				return { number = math.random() }
			end, {})

			return React.createElement(
				"Frame",
				nil,
				React.createElement("TextLabel", {
					Text = "The random number is: " .. tostring(randomNumber.number),
				})
			)
		end

		local shallowRenderer = createRenderer()
		local firstResult = shallowRenderer:render(React.createElement(SomeComponent))
		local secondResult = shallowRenderer:render(React.createElement(SomeComponent))

		jestExpect(firstResult).toEqual(secondResult)
	end)

	it("should work with useMemo with multiple return values", function()
		local capturedNumber1, capturedNumber2
		local function SomeComponent()
			local number1, number2 = React.useMemo(function()
				return math.random(), math.random()
			end, {})

			capturedNumber1, capturedNumber2 = number1, number2

			return React.createElement(
				"Frame",
				nil,
				React.createElement(
					"TextLabel",
					{ Text = tostring(number1) .. " " .. tostring(number2) }
				)
			)
		end

		local shallowRenderer = createRenderer()
		local firstResult = shallowRenderer:render(React.createElement(SomeComponent))
		local secondResult = shallowRenderer:render(React.createElement(SomeComponent))

		jestExpect(firstResult).toEqual(secondResult)
		jestExpect(capturedNumber1).toBeDefined()
		jestExpect(capturedNumber2).toBeDefined()
	end)

	it("should work with useContext", function()
		local SomeContext = React.createContext("default")

		local function SomeComponent()
			local value = React.useContext(SomeContext)

			return React.createElement(
				"Frame",
				nil,
				React.createElement("TextLabel", { Text = tostring(value) })
			)
		end

		local shallowRenderer = createRenderer()
		local result = shallowRenderer:render(React.createElement(SomeComponent))

		jestExpect(result).toEqual(
			React.createElement(
				"Frame",
				nil,
				React.createElement("TextLabel", { Text = "default" })
			)
		)
	end)

	it("should not leak state when component type changes", function()
		local function SomeComponent(props)
			local name = React.useState(props.defaultName)

			return React.createElement("TextLabel", {
				Text = "Your name is: " .. name,
			})
		end

		local function SomeOtherComponent(props)
			local name = React.useState(props.defaultName)

			return React.createElement("TextLabel", {
				Text = "Your name is: " .. name,
			})
		end

		local shallowRenderer = createRenderer()
		local result = shallowRenderer:render(
			React.createElement(SomeComponent, { defaultName = "Dominic" })
		)
		jestExpect(result).toEqual(React.createElement("TextLabel", {
			Text = "Your name is: " .. "Dominic",
		}))

		result = shallowRenderer:render(
			React.createElement(SomeOtherComponent, { defaultName = "Dan" })
		)

		jestExpect(result).toEqual(React.createElement("TextLabel", {
			Text = "Your name is: " .. "Dan",
		}))
	end)

	it("should work with with forwardRef + any hook", function()
		local SomeComponent = React.forwardRef(function(props, ref)
			local randomNumberRef = React.useRef({ number = math.random() })

			return React.createElement(
				"Frame",
				{ ref = ref },
				React.createElement("TextLabel", {
					Text = "The random number is: "
						.. tostring(randomNumberRef.current.number),
				})
			)
		end)

		local shallowRenderer = createRenderer()
		local firstResult = shallowRenderer:render(React.createElement(SomeComponent))
		local secondResult = shallowRenderer:render(React.createElement(SomeComponent))

		jestExpect(firstResult).toEqual(secondResult)
	end)

	it("should update a value from useState outside the render", function()
		local _dispatch

		local function SomeComponent(props)
			local count, dispatch = React.useReducer(function(s, a)
				if a == "inc" then
					return s + 1
				end
				return s
			end, 0)
			local name, updateName = React.useState(props.defaultName)
			_dispatch = function(...)
				return dispatch("inc")
			end

			return React.createElement(
				"Frame",
				{
					onClick = function()
						updateName("Dan")
					end,
				},
				React.createElement("TextLabel", {
					Text = "Your name is: " .. name .. " (" .. count .. ")",
				})
			)
		end

		local shallowRenderer = createRenderer()
		local element = React.createElement(SomeComponent, { defaultName = "Dominic" })
		local result = shallowRenderer:render(element)
		jestExpect(result.props.children).toEqual(
			validateElement(React.createElement("TextLabel", {
				Text = "Your name is: Dominic (0)",
			}))
		)

		result.props.onClick()
		local updated = shallowRenderer:render(element)
		jestExpect(updated.props.children).toEqual(
			validateElement(React.createElement("TextLabel", {
				Text = "Your name is: Dan (0)",
			}))
		)

		_dispatch("foo")
		updated = shallowRenderer:render(element)
		jestExpect(updated.props.children).toEqual(
			validateElement(React.createElement("TextLabel", {
				Text = "Your name is: Dan (1)",
			}))
		)

		_dispatch("inc")
		updated = shallowRenderer:render(element)
		jestExpect(updated.props.children).toEqual(
			validateElement(React.createElement("TextLabel", {
				Text = "Your name is: Dan (2)",
			}))
		)
	end)

	it("should ignore a foreign update outside the render", function()
		local _updateCountForFirstRender

		local function SomeComponent()
			local count, updateCount = React.useState(0)
			if not _updateCountForFirstRender then
				_updateCountForFirstRender = updateCount
			end
			return count
		end

		local shallowRenderer = createRenderer()
		local element = React.createElement(SomeComponent)
		local result = shallowRenderer:render(element)
		jestExpect(result).toEqual(0)
		_updateCountForFirstRender(1)
		result = shallowRenderer:render(element)
		jestExpect(result).toEqual(1)

		shallowRenderer:unmount()
		result = shallowRenderer:render(element)
		jestExpect(result).toEqual(0)
		_updateCountForFirstRender(1) -- Should be ignored.
		result = shallowRenderer:render(element)
		jestExpect(result).toEqual(0)
	end)

	it("should not forget render phase updates", function()
		local _updateCount

		local function SomeComponent()
			local count, updateCount = React.useState(0)
			_updateCount = updateCount
			if count < 5 then
				updateCount(function(x)
					return x + 1
				end)
			end
			return count
		end

		local shallowRenderer = createRenderer()
		local element = React.createElement(SomeComponent)
		local result = shallowRenderer:render(element)
		jestExpect(result).toEqual(5)

		_updateCount(10)
		result = shallowRenderer:render(element)
		jestExpect(result).toEqual(10)

		_updateCount(function(x)
			return x + 1
		end)
		result = shallowRenderer:render(element)
		jestExpect(result).toEqual(11)

		_updateCount(function(x)
			return x - 10
		end)
		result = shallowRenderer:render(element)
		jestExpect(result).toEqual(5)
	end)
end)
