return function()local Y=require(script.Parent.Parent.assertDeepEqual)local i=require(script.Parent.Parent.createElement)local h=require(script.Parent.Parent.createReconciler)local x=require(script.Parent.Parent.createSpy)local C=require(script.Parent.Parent.NoopRenderer)local P=require(script.Parent.Parent.Type)local R=require(script.Parent.Parent.Component)local K=h(C)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 h=R:extend("\077\121\067\111\109\112\111\110\101\110\116")local C local A local q=x(function(Y)C=Y.props A=Y.state return true end)h.shouldUpdate=q.value function h.render(Y)return nil end local k={a=5}local H=i(h,k)local D=nil local N="\084\101\115\116"local Z=K.mountVirtualNode(H,D,N);(expect(q.callCount)).to.equal(0)local J={a=6;b=2}local V=i(h,J)K.updateVirtualNode(Z,V);(expect(q.callCount)).to.equal(1)local X=q:captureValues("\115\101\108\102","\110\101\119\080\114\111\112\115","\110\101\119\083\116\097\116\101");(expect(P.of(X.self))).to.equal(P.StatefulComponentInstance)Y(X.newProps,J)Y(C,k);(expect(X.newState)).to.equal(A)Y(A,{})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 h=R:extend("\077\121\067\111\109\112\111\110\101\110\116")local C={a=1}local A local q function h.init(Y)A=function(...)return Y:setState(...)end Y:setState(C)q=Y.state end local k local H local D=x(function(Y)k=Y.props H=Y.state return true end)h.shouldUpdate=D.value function h.render(Y)return nil end local N=i(h)local Z=nil local J="\084\101\115\116"K.mountVirtualNode(N,Z,J);(expect(D.callCount)).to.equal(0)local V={a=2,b=3}A(V);(expect(D.callCount)).to.equal(1)local X=D:captureValues("\115\101\108\102","\110\101\119\080\114\111\112\115","\110\101\119\083\116\097\116\101");(expect(P.of(X.self))).to.equal(P.StatefulComponentInstance);(expect(X.newProps)).to.equal(k)Y(k,{})Y(H,C);(expect(H)).to.equal(q)Y(X.newState,V)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 Y=R:extend("\077\121\067\111\109\112\111\110\101\110\116")function Y.shouldUpdate(Y)return true end local h=x()Y.render=h.value local C=i(Y)local P=nil local A="\084\101\115\116"local q=K.mountVirtualNode(C,P,A);(expect(h.callCount)).to.equal(1)local k=i(Y)K.updateVirtualNode(q,k);(expect(h.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 Y=R:extend("\077\121\067\111\109\112\111\110\101\110\116")function Y.shouldUpdate(Y)return false end local h=x()Y.render=h.value local C=i(Y)local P=nil local A="\084\101\115\116"local q=K.mountVirtualNode(C,P,A);(expect(h.callCount)).to.equal(1)local k=i(Y)K.updateVirtualNode(q,k);(expect(h.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
