return function()local K=require(script.Parent.Parent.assertDeepEqual)local e=require(script.Parent.Parent.createElement)local B=require(script.Parent.Parent.createReconciler)local M=require(script.Parent.Parent.NoopRenderer)local x=require(script.Parent.Parent.oneChild)local u=require(script.Parent.Parent.Component)local P=B(M)it("\115\104\111\117\108\100 \098\101 \112\114\111\118\105\100\101\100 \097\115 \097\110 \105\110\116\101\114\110\097\108 \097\112\105 \111\110 \067\111\109\112\111\110\101\110\116",function()local B=u:extend("\080\114\111\118\105\100\101\114")function B.init(K)K:__addContext("\102\111\111","\098\097\114")end function B.render(K) end local M=e(B)local x=nil local J="\080\114\111\118\105\100\101\114"local C=P.mountVirtualNode(M,x,J)local g={foo="\098\097\114"}K(C.context,g)end)it("\115\104\111\117\108\100 \098\101 \105\110\104\101\114\105\116\101\100 \102\114\111\109 \112\097\114\101\110\116 \115\116\097\116\101\102\117\108 \110\111\100\101\115",function()local B=u:extend("\067\111\110\115\117\109\101\114")local M function B.init(K)M={hello=K:__getContext("\104\101\108\108\111"),value=K:__getContext("\118\097\108\117\101")}end function B.render(K) end local x=u:extend("\080\097\114\101\110\116")function x.render(K)return e(B)end local J=e(x)local C=nil local g="\080\097\114\101\110\116"local p={hello="\119\111\114\108\100";value=6}local j=P.mountVirtualNode(J,C,g,p);(expect(M)).never.to.equal(p);(expect(M)).never.to.equal(j.context)K(j.context,p)K(M,p)end)it("\115\104\111\117\108\100 \098\101 \105\110\104\101\114\105\116\101\100 \102\114\111\109 \112\097\114\101\110\116 \102\117\110\099\116\105\111\110 \110\111\100\101\115",function()local B=u:extend("\067\111\110\115\117\109\101\114")local M function B.init(K)M={hello=K:__getContext("\104\101\108\108\111"),value=K:__getContext("\118\097\108\117\101")}end function B.render(K) end local function x()return e(B)end local J=e(x)local C=nil local g="\080\097\114\101\110\116"local p={hello="\119\111\114\108\100",value=6}local j=P.mountVirtualNode(J,C,g,p);(expect(M)).never.to.equal(p);(expect(M)).never.to.equal(j.context)K(j.context,p)K(M,p)end)it("\115\104\111\117\108\100 \110\111\116 \099\111\112\121 \116\104\101 \099\111\110\116\101\120\116 \116\097\098\108\101 \105\102 \105\116 \100\111\101\115\110\'\116 \110\101\101\100 \116\111",function()local B=u:extend("\080\097\114\101\110\116")function B.init(K)K:__addContext("\112\097\114\101\110\116","\073\'\109 \104\101\114\101\033")end function B.render(K)return e(function() end)end local M=e(B)local J=nil local C="\080\097\114\101\110\116"local g=P.mountVirtualNode(M,J,C)local p={parent="\073\'\109 \104\101\114\101\033"}K(g.context,p)local j=x(g.children);(expect(g.context)).to.equal(j.context)end)it("\115\104\111\117\108\100 \110\111\116 \097\108\108\111\119 \099\111\110\116\101\120\116 \116\111 \109\111\118\101 \117\112 \116\104\101 \116\114\101\101",function()local B=u:extend("\067\104\105\108\100\080\114\111\118\105\100\101\114")function B.init(K)K:__addContext("\099\104\105\108\100","\073\'\109 \104\101\114\101 \116\111\111\033")end function B.render(K) end local M=u:extend("\080\097\114\101\110\116\080\114\111\118\105\100\101\114")function M.init(K)K:__addContext("\112\097\114\101\110\116","\073\'\109 \104\101\114\101\033")end function M.render(K)return e(B)end local J=e(M)local C=nil local g="\080\097\114\101\110\116"local p=P.mountVirtualNode(J,C,g)local j=x(p.children)local U={parent="\073\'\109 \104\101\114\101\033"}local v={parent="\073\'\109 \104\101\114\101\033",child="\073\'\109 \104\101\114\101 \116\111\111\033"}K(p.context,U)K(j.context,v)end)it("\115\104\111\117\108\100 \099\111\110\116\097\105\110 \118\097\108\117\101\115 \112\117\116 \105\110\116\111 \116\104\101 \116\114\101\101 \098\121 \112\097\114\101\110\116 \110\111\100\101\115",function()local B=u:extend("\067\111\110\115\117\109\101\114")local M function B.init(K)M={dont=K:__getContext("\100\111\110\116");frob=K:__getContext("\102\114\111\098")}end function B.render(K) end local x=u:extend("\080\114\111\118\105\100\101\114")function x.init(K)K:__addContext("\102\114\111\098","\117\108\097\116\111\114")end function x.render(K)return e(B)end local J=e(x)local C=nil local g="\067\111\110\115\117\109\101\114"local p={dont="\116\114\121 \105\116"}local j=P.mountVirtualNode(J,C,g,p)local U={dont="\116\114\121 \105\116"}local v={dont="\116\114\121 \105\116";frob="\117\108\097\116\111\114"};(expect(j.context)).never.to.equal(p);(expect(M)).never.to.equal(p);(expect(M)).never.to.equal(j.context)K(p,U)K(j.context,v)K(M,v)end)it("\115\104\111\117\108\100 \116\114\097\110\115\102\101\114 \099\111\110\116\101\120\116 \116\111 \099\104\105\108\100\114\101\110 \116\104\097\116 \097\114\101 \114\101\112\108\097\099\101\100",function()local B=u:extend("\067\111\110\115\117\109\101\114\065")local function M(K)return{A=K:__getContext("\065"),B=K:__getContext("\066"),frob=K:__getContext("\102\114\111\098")}end local x function B.init(K)K:__addContext("\065","\104\101\108\108\111")x=M(K)end function B.render(K) end local J=u:extend("\067\111\110\115\117\109\101\114\066")local C function J.init(K)K:__addContext("\066","\104\101\108\108\111")C=M(K)end function J.render(K) end local g=u:extend("\080\114\111\118\105\100\101\114")function g.init(K)K:__addContext("\102\114\111\098","\117\108\097\116\111\114")end function g.render(K)local M=K.props.useConsumerB if M then return e(J)else return e(B)end end local p=nil local j="\067\111\110\115\117\109\101\114"local U=e(g)local v=P.mountVirtualNode(U,p,j)local r={frob="\117\108\097\116\111\114",A="\104\101\108\108\111"}K(x,r)local n={frob="\117\108\097\116\111\114";B="\104\101\108\108\111"}local l=e(g,{useConsumerB=true})P.updateVirtualNode(v,l)K(C,n)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 NoopRenderer = require(script.Parent.Parent.NoopRenderer)
	local oneChild = require(script.Parent.Parent.oneChild)

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

	local noopReconciler = createReconciler(NoopRenderer)

	it("should be provided as an internal api on Component", function()
		local Provider = Component:extend("Provider")

		function Provider:init()
			self:__addContext("foo", "bar")
		end

		function Provider:render()
		end

		local element = createElement(Provider)
		local hostParent = nil
		local hostKey = "Provider"
		local node = noopReconciler.mountVirtualNode(element, hostParent, hostKey)

		local expectedContext = {
			foo = "bar",
		}

		assertDeepEqual(node.context, expectedContext)
	end)

	it("should be inherited from parent stateful nodes", function()
		local Consumer = Component:extend("Consumer")

		local capturedContext
		function Consumer:init()
			capturedContext = {
				hello = self:__getContext("hello"),
				value = self:__getContext("value"),
			}
		end

		function Consumer:render()
		end

		local Parent = Component:extend("Parent")

		function Parent:render()
			return createElement(Consumer)
		end

		local element = createElement(Parent)
		local hostParent = nil
		local hostKey = "Parent"
		local context = {
			hello = "world",
			value = 6,
		}
		local node = noopReconciler.mountVirtualNode(element, hostParent, hostKey, context)

		expect(capturedContext).never.to.equal(context)
		expect(capturedContext).never.to.equal(node.context)
		assertDeepEqual(node.context, context)
		assertDeepEqual(capturedContext, context)
	end)

	it("should be inherited from parent function nodes", function()
		local Consumer = Component:extend("Consumer")

		local capturedContext
		function Consumer:init()
			capturedContext = {
				hello = self:__getContext("hello"),
				value = self:__getContext("value"),
			}
		end

		function Consumer:render()
		end

		local function Parent()
			return createElement(Consumer)
		end

		local element = createElement(Parent)
		local hostParent = nil
		local hostKey = "Parent"
		local context = {
			hello = "world",
			value = 6,
		}
		local node = noopReconciler.mountVirtualNode(element, hostParent, hostKey, context)

		expect(capturedContext).never.to.equal(context)
		expect(capturedContext).never.to.equal(node.context)
		assertDeepEqual(node.context, context)
		assertDeepEqual(capturedContext, context)
	end)

	it("should not copy the context table if it doesn't need to", function()
		local Parent = Component:extend("Parent")

		function Parent:init()
			self:__addContext("parent", "I'm here!")
		end

		function Parent:render()
			-- Create some child element
			return createElement(function() end)
		end

		local element = createElement(Parent)
		local hostParent = nil
		local hostKey = "Parent"
		local parentNode = noopReconciler.mountVirtualNode(element, hostParent, hostKey)

		local expectedContext = {
			parent = "I'm here!",
		}

		assertDeepEqual(parentNode.context, expectedContext)

		local childNode = oneChild(parentNode.children)

		-- Parent and child should have the same context table
		expect(parentNode.context).to.equal(childNode.context)
	end)

	it("should not allow context to move up the tree", function()
		local ChildProvider = Component:extend("ChildProvider")

		function ChildProvider:init()
			self:__addContext("child", "I'm here too!")
		end

		function ChildProvider:render()
		end

		local ParentProvider = Component:extend("ParentProvider")

		function ParentProvider:init()
			self:__addContext("parent", "I'm here!")
		end

		function ParentProvider:render()
			return createElement(ChildProvider)
		end

		local element = createElement(ParentProvider)
		local hostParent = nil
		local hostKey = "Parent"

		local parentNode = noopReconciler.mountVirtualNode(element, hostParent, hostKey)
		local childNode = oneChild(parentNode.children)

		local expectedParentContext = {
			parent = "I'm here!",
			-- Context does not travel back up
		}

		local expectedChildContext = {
			parent = "I'm here!",
			child = "I'm here too!"
		}

		assertDeepEqual(parentNode.context, expectedParentContext)
		assertDeepEqual(childNode.context, expectedChildContext)
	end)

	it("should contain values put into the tree by parent nodes", function()
		local Consumer = Component:extend("Consumer")

		local capturedContext
		function Consumer:init()
			capturedContext = {
				dont = self:__getContext("dont"),
				frob = self:__getContext("frob"),
			}
		end

		function Consumer:render()
		end

		local Provider = Component:extend("Provider")

		function Provider:init()
			self:__addContext("frob", "ulator")
		end

		function Provider:render()
			return createElement(Consumer)
		end

		local element = createElement(Provider)
		local hostParent = nil
		local hostKey = "Consumer"
		local context = {
			dont = "try it",
		}
		local node = noopReconciler.mountVirtualNode(element, hostParent, hostKey, context)

		local initialContext = {
			dont = "try it",
		}

		local expectedContext = {
			dont = "try it",
			frob = "ulator",
		}

		-- Because components mutate context, we're careful with equality
		expect(node.context).never.to.equal(context)
		expect(capturedContext).never.to.equal(context)
		expect(capturedContext).never.to.equal(node.context)

		assertDeepEqual(context, initialContext)
		assertDeepEqual(node.context, expectedContext)
		assertDeepEqual(capturedContext, expectedContext)
	end)

	it("should transfer context to children that are replaced", function()
		local ConsumerA = Component:extend("ConsumerA")

		local function captureAllContext(component)
			return {
				A = component:__getContext("A"),
				B = component:__getContext("B"),
				frob = component:__getContext("frob"),
			}
		end

		local capturedContextA
		function ConsumerA:init()
			self:__addContext("A", "hello")

			capturedContextA = captureAllContext(self)
		end

		function ConsumerA:render()
		end

		local ConsumerB = Component:extend("ConsumerB")

		local capturedContextB
		function ConsumerB:init()
			self:__addContext("B", "hello")

			capturedContextB = captureAllContext(self)
		end

		function ConsumerB:render()
		end

		local Provider = Component:extend("Provider")

		function Provider:init()
			self:__addContext("frob", "ulator")
		end

		function Provider:render()
			local useConsumerB = self.props.useConsumerB

			if useConsumerB then
				return createElement(ConsumerB)
			else
				return createElement(ConsumerA)
			end
		end

		local hostParent = nil
		local hostKey = "Consumer"

		local element = createElement(Provider)
		local node = noopReconciler.mountVirtualNode(element, hostParent, hostKey)

		local expectedContextA = {
			frob = "ulator",
			A = "hello",
		}

		assertDeepEqual(capturedContextA, expectedContextA)

		local expectedContextB = {
			frob = "ulator",
			B = "hello",
		}

		local replacedElement = createElement(Provider, {
			useConsumerB = true,
		})
		noopReconciler.updateVirtualNode(node, replacedElement)

		assertDeepEqual(capturedContextB, expectedContextB)
	end)
end