return function()local O=require(script.Parent.Parent.Parent.assertDeepEqual)local z=require(script.Parent.Parent.createElement)local l=require(script.Parent.Parent.createReconciler)local o=require(script.Parent.Parent.createSpy)local r=require(script.Parent.Parent.NoopRenderer)local J=require(script.Parent.Parent.Type)local P=require(script.Parent.Parent.Component)local F=l(r)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 l=P:extend("\077\121\067\111\109\112\111\110\101\110\116")local r local V local L=o(function(O)r=O.props V=O.state return true end)l.shouldUpdate=L.value function l.render(O)return nil end local p={a=5}local Q=z(l,p)local U=nil local j="\084\101\115\116"local h=F.mountVirtualNode(Q,U,j);(expect(L.callCount)).to.equal(0)local M={a=6,b=2}local y=z(l,M)F.updateVirtualNode(h,y);(expect(L.callCount)).to.equal(1)local e=L:captureValues("\115\101\108\102","\110\101\119\080\114\111\112\115","\110\101\119\083\116\097\116\101");(expect(J.of(e.self))).to.equal(J.StatefulComponentInstance)O(e.newProps,M)O(r,p);(expect(e.newState)).to.equal(V)O(V,{})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 l=P:extend("\077\121\067\111\109\112\111\110\101\110\116")local r={a=1}local V local L function l.init(O)V=function(...)return O:setState(...)end O:setState(r)L=O.state end local p local Q local U=o(function(O)p=O.props Q=O.state return true end)l.shouldUpdate=U.value function l.render(O)return nil end local j=z(l)local h=nil local M="\084\101\115\116"F.mountVirtualNode(j,h,M);(expect(U.callCount)).to.equal(0)local y={a=2;b=3}V(y);(expect(U.callCount)).to.equal(1)local e=U:captureValues("\115\101\108\102","\110\101\119\080\114\111\112\115","\110\101\119\083\116\097\116\101");(expect(J.of(e.self))).to.equal(J.StatefulComponentInstance);(expect(e.newProps)).to.equal(p)O(p,{})O(Q,r);(expect(Q)).to.equal(L)O(e.newState,y)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 O=P:extend("\077\121\067\111\109\112\111\110\101\110\116")function O.shouldUpdate(O)return true end local l=o()O.render=l.value local r=z(O)local J=nil local V="\084\101\115\116"local L=F.mountVirtualNode(r,J,V);(expect(l.callCount)).to.equal(1)local p=z(O)F.updateVirtualNode(L,p);(expect(l.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 O=P:extend("\077\121\067\111\109\112\111\110\101\110\116")function O.shouldUpdate(O)return false end local l=o()O.render=l.value local r=z(O)local J=nil local V="\084\101\115\116"local L=F.mountVirtualNode(r,J,V);(expect(l.callCount)).to.equal(1)local p=z(O)F.updateVirtualNode(L,p);(expect(l.callCount)).to.equal(1)end)end
return function()
	local assertDeepEqual = require(script.Parent.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
