local x local s local F local z local W,M,S local p=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 r=p.beforeEach local Q=p.expect local J=p.describe local B=p.it local c=p.jest local l=require("\064\112\107\103\047\064\106\115\100\111\116\108\117\097\047\112\114\111\109\105\115\101")J("\082\101\097\099\116\084\101\115\116\082\101\110\100\101\114\101\114\046\097\099\116\040\041",function()r(function()c.resetModules()c.useRealTimers()x=require("\064\112\107\103\047\064\106\115\100\111\116\108\117\097\047\114\101\097\099\116")W,M,S=x.useState,x.useEffect,x.useReducer s=require("\064\112\107\103\047\064\106\115\100\111\116\108\117\097\047\114\101\097\099\116-\116\101\115\116-\114\101\110\100\101\114\101\114")F=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")z=s.act end)B("\099\097\110 \117\115\101 \046\097\099\116\040\041 \116\111 \102\108\117\115\104 \101\102\102\101\099\116\115",function()local function F(s)local F,z=W(0)x.useEffect(function()s.callback()z(1)end,{})return F end local M={}local S z(function()S=s.create(x.createElement(F,{callback=function()table.insert(M,#M)end}))end);(Q(M)).toEqual({0});(Q(S.toJSON())).toEqual("\049")end)B("\119\097\114\110\115 \105\102 \121\111\117 \100\111\110\'\116 \117\115\101 \046\097\099\116",function()local F,z local function M(x)F,z=W(0)return F end s.create(x.createElement(M));(Q(function()z(1)end)).toErrorDev({"\065\110 \117\112\100\097\116\101 \116\111 \065\112\112 \105\110\115\105\100\101 \097 \116\101\115\116 \119\097\115 \110\111\116 \119\114\097\112\112\101\100 \105\110 \097\099\116\040\046\046\046\041\046"})end)J("\097\115\121\110\099",function()B("\115\104\111\117\108\100 \119\111\114\107 \119\105\116\104 \097\115\121\110\099\047\097\119\097\105\116",function()local F=l.promisify(function(x)return l.resolve({details={1;2,3}})end)local function W()local s,z=x.useState(0)x.useEffect(function()local x=function()return(F()):andThen(function(x)z(x.details)end)end x()end,{})return s end local M;(l.try(function()z(function()M=s.create(x.createElement(W))end)end)):await();(Q(M.toJSON())).toEqual({"\049","\050","\051"})end)B("\115\104\111\117\108\100 \110\111\116 \102\108\117\115\104 \101\102\102\101\099\116\115 \119\105\116\104\111\117\116 \097\108\115\111 \102\108\117\115\104\105\110\103 \109\105\099\114\111\116\097\115\107\115",function()local W=l.resolve()local function p()local x,s=S(function(x)if x==3 then return 3 end return x+1 end,1)M(function()F.unstable_yieldValue("\069\102\102\101\099\116")W:andThen(function()F.unstable_yieldValue("\077\105\099\114\111\116\097\115\107")s()end)end)return x end local r=s.create(nil);(l.try(function()z(function()r.update(x.createElement(p))end)end)):await();(Q(F)).toHaveYielded({"\069\102\102\101\099\116","\077\105\099\114\111\116\097\115\107";"\069\102\102\101\099\116","\077\105\099\114\111\116\097\115\107","\069\102\102\101\099\116","\077\105\099\114\111\116\097\115\107"});(Q(r)).toMatchRenderedOutput("\051")end)end)end)
local React
local ReactTestRenderer
local Scheduler
local act
local useState, useEffect, useReducer

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

local Promise = require("@pkg/@jsdotlua/promise")

describe("ReactTestRenderer.act()", function()
	beforeEach(function()
		jest.resetModules()
		jest.useRealTimers()

		React = require("@pkg/@jsdotlua/react")
		useState, useEffect, useReducer =
			React.useState, React.useEffect, React.useReducer
		ReactTestRenderer = require("@pkg/@jsdotlua/react-test-renderer")
		Scheduler = require("@pkg/@jsdotlua/scheduler")
		act = ReactTestRenderer.act
	end)
	it("can use .act() to flush effects", function()
		local function App(props)
			local ctr, setCtr = useState(0)

			React.useEffect(function()
				props.callback()
				setCtr(1)
			end, {})

			return ctr
		end

		local calledLog = {}
		local root

		act(function()
			root = ReactTestRenderer.create(React.createElement(App, {
				callback = function()
					table.insert(calledLog, #calledLog)
				end,
			}))
		end)
		jestExpect(calledLog).toEqual({ 0 })
		jestExpect(root.toJSON()).toEqual("1")
	end)
	it("warns if you don't use .act", function()
		local ctr, setCtr

		local function App(props)
			ctr, setCtr = useState(0)

			return ctr
		end

		ReactTestRenderer.create(React.createElement(App))

		jestExpect(function()
			setCtr(1)
		end).toErrorDev({
			"An update to App inside a test was not wrapped in act(...).",
		})
	end)
	describe("async", function()
		it("should work with async/await", function()
			local fetch = Promise.promisify(function(url)
				return Promise.resolve({
					details = { 1, 2, 3 },
				})
			end)
			local function App()
				local details, setDetails = React.useState(0)

				React.useEffect(function()
					local fetchDetails = function()
						return fetch():andThen(function(response)
							setDetails(response.details)
						end)
					end

					fetchDetails()
				end, {})

				return details
			end

			local root

			Promise.try(function()
				act(function()
					root = ReactTestRenderer.create(React.createElement(App))
				end)
			end):await()

			jestExpect(root.toJSON()).toEqual({ "1", "2", "3" })
		end)
		it("should not flush effects without also flushing microtasks", function()
			local alreadyResolvedPromise = Promise.resolve()

			local function App()
				-- This component will keep updating itself until step === 3
				local step, proceed = useReducer(function(s)
					if s == 3 then
						return 3
					end

					return s + 1
				end, 1)

				useEffect(function()
					Scheduler.unstable_yieldValue("Effect")
					alreadyResolvedPromise:andThen(function()
						Scheduler.unstable_yieldValue("Microtask")
						proceed()
					end)
				end)

				return step
			end

			local root = ReactTestRenderer.create(nil)

			Promise.try(function()
				act(function()
					root.update(React.createElement(App))
				end)
			end):await()

			jestExpect(Scheduler).toHaveYielded({
				-- Should not flush effects without also flushing microtasks
				-- First render:
				"Effect",
				"Microtask",
				-- Second render:
				"Effect",
				"Microtask",
				-- Final render:
				"Effect",
				"Microtask",
			})

			jestExpect(root).toMatchRenderedOutput("3")
		end)
	end)
end)
