local e local k local p local f local S=require("\064\112\107\103\047\064\106\115\100\111\116\108\117\097\047\106\101\115\116-\103\108\111\098\097\108\115")local A=S.expect local M=S.jest local O=S.beforeEach local X=S.it O(function()M.resetModules()e=require("\064\112\107\103\047\064\106\115\100\111\116\108\117\097\047\114\101\097\099\116")k=require("\064\112\107\103\047\064\106\115\100\111\116\108\117\097\047\114\101\097\099\116-\114\111\098\108\111\120")p=require("\064\112\107\103\047\064\106\115\100\111\116\108\117\097\047\115\099\104\101\100\117\108\101\114")f=require("\046\046\047\082\111\098\108\111\120\067\111\109\112\111\110\101\110\116\080\114\111\112\115")end)local function o(e)local k=0 for e in e do k+=1 end return k end X("\115\104\111\117\108\100 \099\108\101\097\114 \105\110\115\116\097\110\099\101\084\111\066\105\110\100\105\110\103\115 \109\097\112 \111\102 \117\110\109\111\117\110\116\101\100 \105\110\115\116\097\110\099\101\115",function()local S=e.createBinding("\072\101\108\108\111 \119\111\114\108\100\033")local function M()return e.createElement("\084\101\120\116\076\097\098\101\108",{key="\108\097\098\101\108",Text=S})end local O=Instance.new("\070\111\108\100\101\114")local X=k.createRoot(O)X:render(e.createElement(M))p.unstable_flushAllWithoutAsserting();(A(o(f._instanceToBindings))).toBe(1)for e in f._instanceToBindings do(A(e:IsDescendantOf(O))).toBe(true)end X:unmount()p.unstable_flushAllWithoutAsserting();(A(o(f._instanceToBindings))).toBe(0)end)X("\115\104\111\117\108\100 \099\108\101\097\114 \105\110\115\116\097\110\099\101\084\111\069\118\101\110\116\077\097\110\097\103\101\114 \109\097\112 \111\102 \117\110\109\111\117\110\116\101\100 \105\110\115\116\097\110\099\101\115",function()local function S()return e.createElement("\084\101\120\116\066\117\116\116\111\110",{key="\098\117\116\116\111\110";[k.Event.Activated]=function() end;[k.Change.Text]=function() end})end local M=Instance.new("\070\111\108\100\101\114")local O=k.createRoot(M)O:render(e.createElement(S))p.unstable_flushAllWithoutAsserting();(A(o(f._instanceToEventManager))).toBe(1)for e in f._instanceToEventManager do(A(e:IsDescendantOf(M))).toBe(true)end O:unmount()p.unstable_flushAllWithoutAsserting();(A(o(f._instanceToEventManager))).toBe(0)end)X("\115\104\111\117\108\100 \099\108\101\097\114 \105\110\115\116\097\110\099\101\084\111\066\105\110\100\105\110\103\115 \109\097\112 \111\102 \117\110\109\111\117\110\116\101\100 \100\101\115\099\101\110\100\101\110\116\115",function()local S=e.createBinding("\072\101\108\108\111 \119\111\114\108\100\033")local function M()return e.createElement("\070\114\097\109\101",{},{Label=e.createElement("\084\101\120\116\076\097\098\101\108",{Text=S});Button=e.createElement("\084\101\120\116\066\117\116\116\111\110",{Text=S:map(function(e)return e.." \040\066\117\116\116\111\110\041"end)})})end local O=Instance.new("\070\111\108\100\101\114")local X=k.createRoot(O)X:render(e.createElement("\083\099\114\101\101\110\071\117\105",nil,e.createElement(M)))p.unstable_flushAllWithoutAsserting();(A(o(f._instanceToBindings))).toBe(2)for e in f._instanceToBindings do(A(e:IsDescendantOf(O))).toBe(true)end X:unmount()p.unstable_flushAllWithoutAsserting();(A(o(f._instanceToBindings))).toBe(0)end)X("\115\104\111\117\108\100 \099\108\101\097\114 \105\110\115\116\097\110\099\101\084\111\069\118\101\110\116\077\097\110\097\103\101\114 \109\097\112 \111\102 \117\110\109\111\117\110\116\101\100 \100\101\115\099\101\110\100\101\110\116\115",function()local function S()return e.createElement("\070\114\097\109\101",{},{Button=e.createElement("\084\101\120\116\066\117\116\116\111\110",{[k.Event.Activated]=function() end}),Label=e.createElement("\084\101\120\116\076\097\098\101\108",{[k.Change.Text]=function() end})})end local M=Instance.new("\070\111\108\100\101\114")local O=k.createRoot(M)O:render(e.createElement("\083\099\114\101\101\110\071\117\105",nil,e.createElement(S)))p.unstable_flushAllWithoutAsserting();(A(o(f._instanceToEventManager))).toBe(2)for e in f._instanceToEventManager do(A(e:IsDescendantOf(M))).toBe(true)end O:unmount()p.unstable_flushAllWithoutAsserting();(A(o(f._instanceToEventManager))).toBe(0)end)
local React
local ReactRoblox
local Scheduler
local RobloxComponentProps

local JestGlobals = require("@pkg/@jsdotlua/jest-globals")
local jestExpect = JestGlobals.expect
local jest = JestGlobals.jest
local beforeEach = JestGlobals.beforeEach
local it = JestGlobals.it

beforeEach(function()
	jest.resetModules()
	React = require("@pkg/@jsdotlua/react")
	ReactRoblox = require("@pkg/@jsdotlua/react-roblox")
	Scheduler = require("@pkg/@jsdotlua/scheduler")
	RobloxComponentProps = require("../RobloxComponentProps")
end)

local function getSizeOfMap(map)
	local count = 0
	for _ in map do
		count += 1
	end
	return count
end

it("should clear instanceToBindings map of unmounted instances", function()
	local value = React.createBinding("Hello world!")
	local function Component()
		return React.createElement("TextLabel", {
			key = "label",
			Text = value,
		})
	end

	local target = Instance.new("Folder")

	local root = ReactRoblox.createRoot(target)
	root:render(React.createElement(Component))
	Scheduler.unstable_flushAllWithoutAsserting()

	-- There should be one instance in the map
	jestExpect(getSizeOfMap(RobloxComponentProps._instanceToBindings)).toBe(1)

	-- Validate that anything in the map is a mounted instance
	for hostInstance in RobloxComponentProps._instanceToBindings do
		jestExpect(hostInstance:IsDescendantOf(target)).toBe(true)
	end

	root:unmount()
	Scheduler.unstable_flushAllWithoutAsserting()

	jestExpect(getSizeOfMap(RobloxComponentProps._instanceToBindings)).toBe(0)
end)

it("should clear instanceToEventManager map of unmounted instances", function()
	local function Component()
		return React.createElement("TextButton", {
			key = "button",
			[ReactRoblox.Event.Activated] = function()
				-- do something
			end,
			[ReactRoblox.Change.Text] = function()
				-- do something
			end,
		})
	end

	local target = Instance.new("Folder")

	local root = ReactRoblox.createRoot(target)
	root:render(React.createElement(Component))
	Scheduler.unstable_flushAllWithoutAsserting()

	-- There should be one instance in the map
	jestExpect(getSizeOfMap(RobloxComponentProps._instanceToEventManager)).toBe(1)

	-- Validate that anything in the map is a mounted instance
	for hostInstance in RobloxComponentProps._instanceToEventManager do
		jestExpect(hostInstance:IsDescendantOf(target)).toBe(true)
	end

	root:unmount()
	Scheduler.unstable_flushAllWithoutAsserting()

	jestExpect(getSizeOfMap(RobloxComponentProps._instanceToEventManager)).toBe(0)
end)

it("should clear instanceToBindings map of unmounted descendents", function()
	local value = React.createBinding("Hello world!")
	local function Component()
		-- Outer component has no bindings or events, but will get cleaned
		-- up directly by `unmount`
		-- Outer component has no bindings or events, but will get cleaned
		-- up directly by `unmount`
		return React.createElement("Frame", {}, {
			Label = React.createElement("TextLabel", {
				Text = value,
			}),
			Button = React.createElement("TextButton", {
				Text = value:map(function(text)
					return text .. " (Button)"
				end),
			}),
		})
	end

	local target = Instance.new("Folder")

	local root = ReactRoblox.createRoot(target)
	root:render(React.createElement("ScreenGui", nil, React.createElement(Component)))
	Scheduler.unstable_flushAllWithoutAsserting()

	-- There should be one instance in the map
	jestExpect(getSizeOfMap(RobloxComponentProps._instanceToBindings)).toBe(2)

	-- Validate that anything in the map is a mounted instance
	for hostInstance in RobloxComponentProps._instanceToBindings do
		jestExpect(hostInstance:IsDescendantOf(target)).toBe(true)
	end

	root:unmount()
	Scheduler.unstable_flushAllWithoutAsserting()

	jestExpect(getSizeOfMap(RobloxComponentProps._instanceToBindings)).toBe(0)
end)

it("should clear instanceToEventManager map of unmounted descendents", function()
	local function Component()
		-- Outer component has no bindings or events, but will get cleaned
		-- up directly by `unmount`
		return React.createElement("Frame", {}, {
			Button = React.createElement("TextButton", {
				[ReactRoblox.Event.Activated] = function()
					-- do something
				end,
			}),
			Label = React.createElement("TextLabel", {
				[ReactRoblox.Change.Text] = function()
					-- do something
				end,
			}),
		})
	end

	local target = Instance.new("Folder")

	local root = ReactRoblox.createRoot(target)
	root:render(React.createElement("ScreenGui", nil, React.createElement(Component)))
	Scheduler.unstable_flushAllWithoutAsserting()

	-- There should be one instance in the map
	jestExpect(getSizeOfMap(RobloxComponentProps._instanceToEventManager)).toBe(2)

	-- Validate that anything in the map is a mounted instance
	for hostInstance in RobloxComponentProps._instanceToEventManager do
		jestExpect(hostInstance:IsDescendantOf(target)).toBe(true)
	end

	root:unmount()
	Scheduler.unstable_flushAllWithoutAsserting()

	jestExpect(getSizeOfMap(RobloxComponentProps._instanceToEventManager)).toBe(0)
end)
