local React local ReactTestRenderer local Scheduler local act local useState, useEffect, useReducer local Packages = script.Parent.Parent.Parent local JestGlobals = require(Packages.Dev.JestGlobals) local beforeEach = JestGlobals.beforeEach local jestExpect = JestGlobals.expect local describe = JestGlobals.describe local it = JestGlobals.it local jest = JestGlobals.jest local Promise = require(Packages.Dev.Promise) describe("ReactTestRenderer.act()", function() beforeEach(function() jest.resetModules() jest.useRealTimers() React = require(Packages.React) useState, useEffect, useReducer = React.useState, React.useEffect, React.useReducer ReactTestRenderer = require(Packages.ReactTestRenderer) Scheduler = require(Packages.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)