return function()local W=require(script.Parent.Parent.Parent.TableUtils.assign)local R=require(script.Parent.Parent.createElement)local z=require(script.Parent.Parent.createRef)local b=require(script.Parent.Parent.createReconciler)local w=require(script.Parent.Parent.Component)local I=require(script.Parent.Parent.GlobalConfig)local l=require(script.Parent.Parent.PropMarkers.Ref)local i=require(script.Parent.Parent.RobloxRenderer)local Q=require(script.Parent.Parent.forwardRef)local F=b(i)it("\115\104\111\117\108\100 \117\112\100\097\116\101 \114\101\102\115 \119\104\101\110 \115\119\105\116\099\104\105\110\103 \098\101\116\119\101\101\110 \099\104\105\108\100\114\101\110",function()local function b(W)local z=W.forwardedRef local b=W.setRefOnDiv local w,I if b then w=z else I=z end return R("\070\114\097\109\101",nil,{First=R("\070\114\097\109\101",{[l]=w},{Child=R("\084\101\120\116\076\097\098\101\108",{Text="\070\105\114\115\116"})}),Second=R("\083\099\114\111\108\108\105\110\103\070\114\097\109\101",{[l]=I},{Child=R("\084\101\120\116\076\097\098\101\108",{Text="\083\101\099\111\110\100"})})})end local w=Q(function(z,w)return R(b,W({},z,{forwardedRef=w}))end)local I=z()local i=R(w,{[l]=I,setRefOnDiv=true})local U=F.mountVirtualTree(i,nil,"\115\119\105\116\099\104 \114\101\102\115");(expect(I.current.ClassName)).to.equal("\070\114\097\109\101")F.unmountVirtualTree(U)i=R(w,{[l]=I;setRefOnDiv=false})U=F.mountVirtualTree(i,nil,"\115\119\105\116\099\104 \114\101\102\115");(expect(I.current.ClassName)).to.equal("\083\099\114\111\108\108\105\110\103\070\114\097\109\101")F.unmountVirtualTree(U)end)it("\115\104\111\117\108\100 \115\117\112\112\111\114\116 \114\101\110\100\101\114\105\110\103 \110\105\108",function()local W=Q(function(W,R)return nil end)local b=z()local w=R(W,{[l]=b})local I=F.mountVirtualTree(w,nil,"\110\105\108 \114\101\102");(expect(b.current)).to.equal(nil)F.unmountVirtualTree(I)end)it("\115\104\111\117\108\100 \115\117\112\112\111\114\116 \114\101\110\100\101\114\105\110\103 \110\105\108 \102\111\114 \109\117\108\116\105\112\108\101 \099\104\105\108\100\114\101\110",function()local W=Q(function(W,R)return nil end)local b=z()local w=R("\070\114\097\109\101",nil,{NoRef1=R("\070\114\097\109\101");WithRef=R(W,{[l]=b});NoRef2=R("\070\114\097\109\101")})local I=F.mountVirtualTree(w,nil,"\109\117\108\116\105\112\108\101 \099\104\105\108\100\114\101\110 \110\105\108 \114\101\102");(expect(b.current)).to.equal(nil)F.unmountVirtualTree(I)end)itSKIP("\115\104\111\117\108\100 \115\117\112\112\111\114\116 \100\101\102\097\117\108\116\080\114\111\112\115",function()local function b(W)local z=W.forwardedRef local b=W.optional local w=W.required return R("\070\114\097\109\101",{[l]=z},{OptionalChild=b,RequiredChild=w})end local w=Q(function(z,w)return R(b,W({},z,{forwardedRef=w}))end)w.defaultProps={optional=R("\084\101\120\116\076\097\098\101\108")}local I=z()local i=R(w,{[l]=I,optional=R("\070\114\097\109\101"),required=R("\083\099\114\111\108\108\105\110\103\070\114\097\109\101")})local U=F.mountVirtualTree(i,nil,"\119\105\116\104 \111\112\116\105\111\110\097\108");(expect((I.current:FindFirstChild("\079\112\116\105\111\110\097\108\067\104\105\108\100")).ClassName)).to.equal("\070\114\097\109\101");(expect((I.current:FindFirstChild("\082\101\113\117\105\114\101\100\067\104\105\108\100")).ClassName)).to.equal("\083\099\114\111\108\108\105\110\103\070\114\097\109\101")F.unmountVirtualTree(U)i=R(w,{[l]=I;required=R("\083\099\114\111\108\108\105\110\103\070\114\097\109\101")})U=F.mountVirtualTree(i,nil,"\119\105\116\104 \100\101\102\097\117\108\116");(expect((I.current:FindFirstChild("\079\112\116\105\111\110\097\108\067\104\105\108\100")).ClassName)).to.equal("\084\101\120\116\076\097\098\101\108");(expect((I.current:FindFirstChild("\082\101\113\117\105\114\101\100\067\104\105\108\100")).ClassName)).to.equal("\083\099\114\111\108\108\105\110\103\070\114\097\109\101")F.unmountVirtualTree(U)end)it("\115\104\111\117\108\100 \101\114\114\111\114 \105\102 \110\111\116 \112\114\111\118\105\100\101\100 \097 \099\097\108\108\098\097\099\107 \119\104\101\110 \116\121\112\101 \099\104\101\099\107\105\110\103 \105\115 \101\110\097\098\108\101\100",function()I.scoped({typeChecks=true},function()(expect(function()Q(nil)end)).to.throw()end)I.scoped({typeChecks=true},function()(expect(function()Q("\102\111\111")end)).to.throw()end)end)it("\115\104\111\117\108\100 \119\111\114\107 \119\105\116\104\111\117\116 \097 \114\101\102 \116\111 \098\101 \102\111\114\119\097\114\100\101\100",function()local function z()return nil end local function b(b)return R(z,W({},b,{[l]=b.forwardedRef}))end local w=Q(function(z,w)return R(b,W({},z,{forwardedRef=w}))end)local I=R(w,{value=123})local i=F.mountVirtualTree(I,nil,"\110\105\108 \114\101\102")F.unmountVirtualTree(i)end)it("\115\104\111\117\108\100 \102\111\114\119\097\114\100 \097 \114\101\102 \102\111\114 \097 \115\105\110\103\108\101 \099\104\105\108\100",function()local b local function w(W)b=W.value return R("\070\114\097\109\101",{[l]=W[l]})end local function I(z)return R(w,W({},z,{[l]=z.forwardedRef}))end local i=Q(function(z,b)return R(I,W({},z,{forwardedRef=b}))end)local U=z()local p=R(i,{[l]=U,value=123})local x=F.mountVirtualTree(p,nil,"\115\105\110\103\108\101 \099\104\105\108\100 \114\101\102");(expect(b)).to.equal(123);(expect(U.current.ClassName)).to.equal("\070\114\097\109\101")F.unmountVirtualTree(x)end)it("\115\104\111\117\108\100 \102\111\114\119\097\114\100 \097 \114\101\102 \102\111\114 \109\117\108\116\105\112\108\101 \099\104\105\108\100\114\101\110",function()local function b(W)return R("\070\114\097\109\101",{[l]=W[l]})end local function w(z)return R(b,W({},z,{[l]=z.forwardedRef}))end local I=Q(function(z,b)return R(w,W({},z,{forwardedRef=b}))end)local i=z()local U=R("\070\114\097\109\101",nil,{NoRef1=R("\070\114\097\109\101");WithRef=R(I,{[l]=i}),NoRef2=R("\070\114\097\109\101")})local p=F.mountVirtualTree(U,nil,"\109\117\108\116\105 \099\104\105\108\100 \114\101\102");(expect(i.current.ClassName)).to.equal("\070\114\097\109\101")F.unmountVirtualTree(p)end)it("\115\104\111\117\108\100 \109\097\105\110\116\097\105\110 \099\104\105\108\100 \105\110\115\116\097\110\099\101 \097\110\100 \114\101\102 \116\104\114\111\117\103\104 \117\112\100\097\116\101\115",function()local z local function b(W)z=W.value return R("\070\114\097\109\101",{[l]=W[l]})end local function w(z)return R(b,W({},z,{[l]=z.forwardedRef}))end local I=Q(function(z,b)return R(w,W({},z,{forwardedRef=b}))end)local i=0 local U local p=function(W)i=i+1 U=W end local x=R(I,{[l]=p;value=123})local m=F.mountVirtualTree(x,nil,"\109\097\105\110\116\097\105\110\115 \105\110\115\116\097\110\099\101");(expect(z)).to.equal(123);(expect(U.ClassName)).to.equal("\070\114\097\109\101");(expect(i)).to.equal(1)x=R(I,{[l]=p,value=456})m=F.updateVirtualTree(m,x);(expect(z)).to.equal(456);(expect(i)).to.equal(1)F.unmountVirtualTree(m)end)it("\115\104\111\117\108\100 \110\111\116 \114\101-\114\117\110 \116\104\101 \114\101\110\100\101\114 \099\097\108\108\098\097\099\107 \111\110 \097 \100\101\101\112 \115\101\116\083\116\097\116\101",function()local z local b={}local I=w:extend("\073\110\110\101\114")function I.render(W)table.insert(b,"\073\110\110\101\114")z=W return R("\070\114\097\109\101",{[l]=W.props.forwardedRef})end local function i(W)table.insert(b,"\077\105\100\100\108\101")return R(I,W)end local U=Q(function(z,w)table.insert(b,"\070\111\114\119\097\114\100")return R(i,W({},z,{forwardedRef=w}))end)local function p()table.insert(b,"\065\112\112")return R(U)end local x=F.mountVirtualTree(R(p),nil,"\100\101\101\112 \115\101\116\083\116\097\116\101");(expect(#b)).to.equal(4);(expect(b[1])).to.equal("\065\112\112");(expect(b[2])).to.equal("\070\111\114\119\097\114\100");(expect(b[3])).to.equal("\077\105\100\100\108\101");(expect(b[4])).to.equal("\073\110\110\101\114")b={}z:setState({});(expect(#b)).to.equal(1);(expect(b[1])).to.equal("\073\110\110\101\114")F.unmountVirtualTree(x)end)it("\115\104\111\117\108\100 \110\111\116 \105\110\099\108\117\100\101 \116\104\101 \114\101\102 \105\110 \116\104\101 \102\111\114\119\097\114\100\101\100 \112\114\111\112\115",function()local b local function w(W)b=W return R("\070\114\097\109\101",{[l]=W.forwardedRef})end local I=Q(function(z,b)return R(w,W({},z,{forwardedRef=b}))end)local i=z()local U=R(I,{[l]=i})local p=F.mountVirtualTree(U,nil,"\110\111 \114\101\102 \105\110 \112\114\111\112\115");(expect(b)).to.be.ok();(expect(b.forwardedRef)).to.equal(i);(expect(b[l])).to.equal(nil)F.unmountVirtualTree(p)end)end
-- Tests loosely adapted from those found at:
-- * https://github.com/facebook/react/blob/v17.0.1/packages/react/src/__tests__/forwardRef-test.js
-- * https://github.com/facebook/react/blob/v17.0.1/packages/react/src/__tests__/forwardRef-test.internal.js
return function()
	local assign = require(script.Parent.Parent.Parent.TableUtils.assign)
	local createElement = require(script.Parent.Parent.createElement)
	local createRef = require(script.Parent.Parent.createRef)
	local createReconciler = require(script.Parent.Parent.createReconciler)
	local Component = require(script.Parent.Parent.Component)
	local GlobalConfig = require(script.Parent.Parent.GlobalConfig)
	local Ref = require(script.Parent.Parent.PropMarkers.Ref)
	local RobloxRenderer = require(script.Parent.Parent.RobloxRenderer)

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

	local reconciler = createReconciler(RobloxRenderer)

	it("should update refs when switching between children", function()
		local function FunctionComponent(props)
			local forwardedRef = props.forwardedRef
			local setRefOnDiv = props.setRefOnDiv
			-- deviation: clearer to express this way, since we don't have real
			-- ternaries
			local firstRef, secondRef
			if setRefOnDiv then
				firstRef = forwardedRef
			else
				secondRef = forwardedRef
			end
			return createElement("Frame", nil, {
				First = createElement("Frame", {
					[Ref] = firstRef,
				}, {
					Child = createElement("TextLabel", {
						Text = "First",
					}),
				}),
				Second = createElement("ScrollingFrame", {
					[Ref] = secondRef,
				}, {
					Child = createElement("TextLabel", {
						Text = "Second",
					}),
				}),
			})
		end

		local RefForwardingComponent = forwardRef(function(props, ref)
			return createElement(FunctionComponent, assign({}, props, { forwardedRef = ref }))
		end)

		local ref = createRef()

		local element = createElement(RefForwardingComponent, {
			[Ref] = ref,
			setRefOnDiv = true,
		})
		local tree = reconciler.mountVirtualTree(element, nil, "switch refs")
		expect(ref.current.ClassName).to.equal("Frame")
		reconciler.unmountVirtualTree(tree)

		element = createElement(RefForwardingComponent, {
			[Ref] = ref,
			setRefOnDiv = false,
		})
		tree = reconciler.mountVirtualTree(element, nil, "switch refs")
		expect(ref.current.ClassName).to.equal("ScrollingFrame")
		reconciler.unmountVirtualTree(tree)
	end)

	it("should support rendering nil", function()
		local RefForwardingComponent = forwardRef(function(_props, _ref)
			return nil
		end)

		local ref = createRef()

		local element = createElement(RefForwardingComponent, { [Ref] = ref })
		local tree = reconciler.mountVirtualTree(element, nil, "nil ref")
		expect(ref.current).to.equal(nil)
		reconciler.unmountVirtualTree(tree)
	end)

	it("should support rendering nil for multiple children", function()
		local RefForwardingComponent = forwardRef(function(_props, _ref)
			return nil
		end)

		local ref = createRef()

		local element = createElement("Frame", nil, {
			NoRef1 = createElement("Frame"),
			WithRef = createElement(RefForwardingComponent, { [Ref] = ref }),
			NoRef2 = createElement("Frame"),
		})
		local tree = reconciler.mountVirtualTree(element, nil, "multiple children nil ref")
		expect(ref.current).to.equal(nil)
		reconciler.unmountVirtualTree(tree)
	end)

	-- We could support this by having forwardRef return a stateful component,
	-- but it's likely not necessary
	itSKIP("should support defaultProps", function()
		local function FunctionComponent(props)
			local forwardedRef = props.forwardedRef
			local optional = props.optional
			local required = props.required
			return createElement("Frame", {
				[Ref] = forwardedRef,
			}, {
				OptionalChild = optional,
				RequiredChild = required,
			})
		end

		local RefForwardingComponent = forwardRef(function(props, ref)
			return createElement(
				FunctionComponent,
				assign({}, props, {
					forwardedRef = ref,
				})
			)
		end)
		RefForwardingComponent.defaultProps = {
			optional = createElement("TextLabel"),
		}

		local ref = createRef()

		local element = createElement(RefForwardingComponent, {
			[Ref] = ref,
			optional = createElement("Frame"),
			required = createElement("ScrollingFrame"),
		})

		local tree = reconciler.mountVirtualTree(element, nil, "with optional")

		expect(ref.current:FindFirstChild("OptionalChild").ClassName).to.equal("Frame")
		expect(ref.current:FindFirstChild("RequiredChild").ClassName).to.equal("ScrollingFrame")

		reconciler.unmountVirtualTree(tree)
		element = createElement(RefForwardingComponent, {
			[Ref] = ref,
			required = createElement("ScrollingFrame"),
		})
		tree = reconciler.mountVirtualTree(element, nil, "with default")

		expect(ref.current:FindFirstChild("OptionalChild").ClassName).to.equal("TextLabel")
		expect(ref.current:FindFirstChild("RequiredChild").ClassName).to.equal("ScrollingFrame")
		reconciler.unmountVirtualTree(tree)
	end)

	it("should error if not provided a callback when type checking is enabled", function()
		GlobalConfig.scoped({
			typeChecks = true,
		}, function()
			expect(function()
				forwardRef(nil)
			end).to.throw()
		end)

		GlobalConfig.scoped({
			typeChecks = true,
		}, function()
			expect(function()
				forwardRef("foo")
			end).to.throw()
		end)
	end)

	it("should work without a ref to be forwarded", function()
		local function Child()
			return nil
		end

		local function Wrapper(props)
			return createElement(Child, assign({}, props, { [Ref] = props.forwardedRef }))
		end

		local RefForwardingComponent = forwardRef(function(props, ref)
			return createElement(Wrapper, assign({}, props, { forwardedRef = ref }))
		end)

		local element = createElement(RefForwardingComponent, { value = 123 })
		local tree = reconciler.mountVirtualTree(element, nil, "nil ref")
		reconciler.unmountVirtualTree(tree)
	end)

	it("should forward a ref for a single child", function()
		local value
		local function Child(props)
			value = props.value
			return createElement("Frame", {
				[Ref] = props[Ref],
			})
		end

		local function Wrapper(props)
			return createElement(Child, assign({}, props, { [Ref] = props.forwardedRef }))
		end

		local RefForwardingComponent = forwardRef(function(props, ref)
			return createElement(Wrapper, assign({}, props, { forwardedRef = ref }))
		end)

		local ref = createRef()

		local element = createElement(RefForwardingComponent, { [Ref] = ref, value = 123 })
		local tree = reconciler.mountVirtualTree(element, nil, "single child ref")
		expect(value).to.equal(123)
		expect(ref.current.ClassName).to.equal("Frame")
		reconciler.unmountVirtualTree(tree)
	end)

	it("should forward a ref for multiple children", function()
		local function Child(props)
			return createElement("Frame", {
				[Ref] = props[Ref],
			})
		end

		local function Wrapper(props)
			return createElement(Child, assign({}, props, { [Ref] = props.forwardedRef }))
		end

		local RefForwardingComponent = forwardRef(function(props, ref)
			return createElement(Wrapper, assign({}, props, { forwardedRef = ref }))
		end)

		local ref = createRef()

		local element = createElement("Frame", nil, {
			NoRef1 = createElement("Frame"),
			WithRef = createElement(RefForwardingComponent, { [Ref] = ref }),
			NoRef2 = createElement("Frame"),
		})
		local tree = reconciler.mountVirtualTree(element, nil, "multi child ref")
		expect(ref.current.ClassName).to.equal("Frame")
		reconciler.unmountVirtualTree(tree)
	end)

	it("should maintain child instance and ref through updates", function()
		local value
		local function Child(props)
			value = props.value
			return createElement("Frame", {
				[Ref] = props[Ref],
			})
		end

		local function Wrapper(props)
			return createElement(Child, assign({}, props, { [Ref] = props.forwardedRef }))
		end

		local RefForwardingComponent = forwardRef(function(props, ref)
			return createElement(Wrapper, assign({}, props, { forwardedRef = ref }))
		end)

		local setRefCount = 0
		local refValue

		local setRef = function(r)
			setRefCount = setRefCount + 1
			refValue = r
		end

		local element = createElement(RefForwardingComponent, { [Ref] = setRef, value = 123 })
		local tree = reconciler.mountVirtualTree(element, nil, "maintains instance")

		expect(value).to.equal(123)
		expect(refValue.ClassName).to.equal("Frame")
		expect(setRefCount).to.equal(1)

		element = createElement(RefForwardingComponent, { [Ref] = setRef, value = 456 })
		tree = reconciler.updateVirtualTree(tree, element)

		expect(value).to.equal(456)
		expect(setRefCount).to.equal(1)
		reconciler.unmountVirtualTree(tree)
	end)

	it("should not re-run the render callback on a deep setState", function()
		local inst
		local renders = {}

		local Inner = Component:extend("Inner")
		function Inner:render()
			table.insert(renders, "Inner")
			inst = self
			return createElement("Frame", { [Ref] = self.props.forwardedRef })
		end

		local function Middle(props)
			table.insert(renders, "Middle")
			return createElement(Inner, props)
		end

		local Forward = forwardRef(function(props, ref)
			table.insert(renders, "Forward")
			return createElement(Middle, assign({}, props, { forwardedRef = ref }))
		end)

		local function App()
			table.insert(renders, "App")
			return createElement(Forward)
		end

		local tree = reconciler.mountVirtualTree(createElement(App), nil, "deep setState")
		expect(#renders).to.equal(4)
		expect(renders[1]).to.equal("App")
		expect(renders[2]).to.equal("Forward")
		expect(renders[3]).to.equal("Middle")
		expect(renders[4]).to.equal("Inner")

		renders = {}
		inst:setState({})
		expect(#renders).to.equal(1)
		expect(renders[1]).to.equal("Inner")
		reconciler.unmountVirtualTree(tree)
	end)

	it("should not include the ref in the forwarded props", function()
		local capturedProps
		local function CaptureProps(props)
			capturedProps = props
			return createElement("Frame", { [Ref] = props.forwardedRef })
		end

		local RefForwardingComponent = forwardRef(function(props, ref)
			return createElement(CaptureProps, assign({}, props, { forwardedRef = ref }))
		end)

		local ref = createRef()
		local element = createElement(RefForwardingComponent, {
			[Ref] = ref,
		})

		local tree = reconciler.mountVirtualTree(element, nil, "no ref in props")
		expect(capturedProps).to.be.ok()
		expect(capturedProps.forwardedRef).to.equal(ref)
		expect(capturedProps[Ref]).to.equal(nil)
		reconciler.unmountVirtualTree(tree)
	end)
end
