return function()local R=require(script.Parent.Parent.assertDeepEqual)local z=require(script.Parent.Parent.createElement)local J=require(script.Parent.Parent.createReconciler)local f=require(script.Parent.Parent.createSpy)local Q=require(script.Parent.Parent.NoopRenderer)local X=require(script.Parent.Parent.Type)local O=require(script.Parent.Parent.Component)local I=J(Q)it("\115\104\111\117\108\100 \098\101 \105\110\118\111\107\101\100 \119\104\101\110 \112\114\111\112\115 \117\112\100\097\116\101",function()local J=O:extend("\077\121\067\111\109\112\111\110\101\110\116")local Q local C local Y=f(function(R)Q=R.props C=R.state return true end)J.shouldUpdate=Y.value function J.render(R)return nil end local c={a=5}local U=z(J,c)local A=nil local p="\084\101\115\116"local t=I.mountVirtualNode(U,A,p);(expect(Y.callCount)).to.equal(0)local g={a=6,b=2}local T=z(J,g)I.updateVirtualNode(t,T);(expect(Y.callCount)).to.equal(1)local d=Y:captureValues("\115\101\108\102","\110\101\119\080\114\111\112\115","\110\101\119\083\116\097\116\101");(expect(X.of(d.self))).to.equal(X.StatefulComponentInstance)R(d.newProps,g)R(Q,c);(expect(d.newState)).to.equal(C)R(C,{})end)it("\115\104\111\117\108\100 \098\101 \105\110\118\111\107\101\100 \119\104\101\110 \115\116\097\116\101 \105\115 \117\112\100\097\116\101\100",function()local J=O:extend("\077\121\067\111\109\112\111\110\101\110\116")local Q={a=1}local C local Y function J.init(R)C=function(...)return R:setState(...)end R:setState(Q)Y=R.state end local c local U local A=f(function(R)c=R.props U=R.state return true end)J.shouldUpdate=A.value function J.render(R)return nil end local p=z(J)local t=nil local g="\084\101\115\116"I.mountVirtualNode(p,t,g);(expect(A.callCount)).to.equal(0)local T={a=2,b=3}C(T);(expect(A.callCount)).to.equal(1)local d=A:captureValues("\115\101\108\102","\110\101\119\080\114\111\112\115","\110\101\119\083\116\097\116\101");(expect(X.of(d.self))).to.equal(X.StatefulComponentInstance);(expect(d.newProps)).to.equal(c)R(c,{})R(U,Q);(expect(U)).to.equal(Y)R(d.newState,T)end)it("\115\104\111\117\108\100 \110\111\116 \097\098\111\114\116 \097\110 \117\112\100\097\116\101 \119\104\101\110 \114\101\116\117\114\110\105\110\103 \116\114\117\101",function()local R=O:extend("\077\121\067\111\109\112\111\110\101\110\116")function R.shouldUpdate(R)return true end local J=f()R.render=J.value local Q=z(R)local X=nil local C="\084\101\115\116"local Y=I.mountVirtualNode(Q,X,C);(expect(J.callCount)).to.equal(1)local c=z(R)I.updateVirtualNode(Y,c);(expect(J.callCount)).to.equal(2)end)it("\115\104\111\117\108\100 \097\098\111\114\116 \097\110 \117\112\100\097\116\101 \119\104\101\110 \114\101\116\117\110\105\110\103 \102\097\108\115\101",function()local R=O:extend("\077\121\067\111\109\112\111\110\101\110\116")function R.shouldUpdate(R)return false end local J=f()R.render=J.value local Q=z(R)local X=nil local C="\084\101\115\116"local Y=I.mountVirtualNode(Q,X,C);(expect(J.callCount)).to.equal(1)local c=z(R)I.updateVirtualNode(Y,c);(expect(J.callCount)).to.equal(1)end)end
return function()
	local assertDeepEqual = require(script.Parent.Parent.assertDeepEqual)
	local createElement = require(script.Parent.Parent.createElement)
	local createReconciler = require(script.Parent.Parent.createReconciler)
	local createSpy = require(script.Parent.Parent.createSpy)
	local NoopRenderer = require(script.Parent.Parent.NoopRenderer)
	local Type = require(script.Parent.Parent.Type)

	local Component = require(script.Parent.Parent.Component)

	local noopReconciler = createReconciler(NoopRenderer)

	it("should be invoked when props update", function()
		local MyComponent = Component:extend("MyComponent")

		local capturedProps
		local capturedState
		local shouldUpdateSpy = createSpy(function(self)
			capturedProps = self.props
			capturedState = self.state

			return true
		end)

		MyComponent.shouldUpdate = shouldUpdateSpy.value

		function MyComponent:render()
			return nil
		end

		local initialProps = {
			a = 5,
		}
		local initialElement = createElement(MyComponent, initialProps)
		local hostParent = nil
		local key = "Test"

		local node = noopReconciler.mountVirtualNode(initialElement, hostParent, key)

		expect(shouldUpdateSpy.callCount).to.equal(0)

		local newProps = {
			a = 6,
			b = 2,
		}
		local newElement = createElement(MyComponent, newProps)
		noopReconciler.updateVirtualNode(node, newElement)

		expect(shouldUpdateSpy.callCount).to.equal(1)

		local values = shouldUpdateSpy:captureValues("self", "newProps", "newState")

		expect(Type.of(values.self)).to.equal(Type.StatefulComponentInstance)

		assertDeepEqual(values.newProps, newProps)

		assertDeepEqual(capturedProps, initialProps)

		expect(values.newState).to.equal(capturedState)
		assertDeepEqual(capturedState, {})
	end)

	it("should be invoked when state is updated", function()
		local MyComponent = Component:extend("MyComponent")

		local initialState = {
			a = 1,
		}

		local setState
		local initState
		function MyComponent:init()
			setState = function(...)
				return self:setState(...)
			end

			self:setState(initialState)

			initState = self.state
		end

		local capturedProps
		local capturedState
		local shouldUpdateSpy = createSpy(function(self)
			capturedProps = self.props
			capturedState = self.state

			return true
		end)

		MyComponent.shouldUpdate = shouldUpdateSpy.value

		function MyComponent:render()
			return nil
		end

		local initialElement = createElement(MyComponent)
		local hostParent = nil
		local key = "Test"

		noopReconciler.mountVirtualNode(initialElement, hostParent, key)

		expect(shouldUpdateSpy.callCount).to.equal(0)

		local newState = {
			a = 2,
			b = 3,
		}

		setState(newState)

		expect(shouldUpdateSpy.callCount).to.equal(1)

		local values = shouldUpdateSpy:captureValues("self", "newProps", "newState")

		expect(Type.of(values.self)).to.equal(Type.StatefulComponentInstance)

		expect(values.newProps).to.equal(capturedProps)
		assertDeepEqual(capturedProps, {})

		assertDeepEqual(capturedState, initialState)
		expect(capturedState).to.equal(initState)
		assertDeepEqual(values.newState, newState)
	end)

	it("should not abort an update when returning true", function()
		local MyComponent = Component:extend("MyComponent")

		function MyComponent:shouldUpdate()
			return true
		end

		local renderSpy = createSpy()

		MyComponent.render = renderSpy.value

		local initialElement = createElement(MyComponent)
		local hostParent = nil
		local key = "Test"

		local node = noopReconciler.mountVirtualNode(initialElement, hostParent, key)

		expect(renderSpy.callCount).to.equal(1)

		local newElement = createElement(MyComponent)
		noopReconciler.updateVirtualNode(node, newElement)

		expect(renderSpy.callCount).to.equal(2)
	end)

	it("should abort an update when retuning false", function()
		local MyComponent = Component:extend("MyComponent")

		function MyComponent:shouldUpdate()
			return false
		end

		local renderSpy = createSpy()

		MyComponent.render = renderSpy.value

		local initialElement = createElement(MyComponent)
		local hostParent = nil
		local key = "Test"

		local node = noopReconciler.mountVirtualNode(initialElement, hostParent, key)

		expect(renderSpy.callCount).to.equal(1)

		local newElement = createElement(MyComponent)
		noopReconciler.updateVirtualNode(node, newElement)

		expect(renderSpy.callCount).to.equal(1)
	end)
end
