local e=(require("\064\112\107\103\047\064\106\115\100\111\116\108\117\097\047\115\104\097\114\101\100")).ReactVersion local k=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 p=k.expect local f=k.beforeEach local S=k.afterEach local A=k.it local M=k.xit local O=k.jest local X=k.describe local o=require("\064\112\107\103\047\064\106\115\100\111\116\108\117\097\047\108\117\097\117-\112\111\108\121\102\105\108\108")local L=o.Array local r=o.Error local q=require("\064\112\107\103\047\064\106\115\100\111\116\108\117\097\047\112\114\111\109\105\115\101")X("\083\099\104\101\100\117\108\105\110\103\080\114\111\102\105\108\101\114",function()local k local X local o local K local h local function a()return{mark=function(e)table.insert(h,e)end}end f(function()O.resetModules()_G.performance=a()h={}local e=(require("\064\112\107\103\047\064\106\115\100\111\116\108\117\097\047\115\104\097\114\101\100")).ReactFeatureFlags e.enableSchedulingProfiler=true e.enableProfilerTimer=true e.replayFailedUnitOfWorkWithInvokeGuardedCallback=true e.enableSuspenseServerRenderer=true e.decoupleUpdatePriorityFromScheduler=true e.debugRenderPhaseSideEffectsForStrictMode=false e.enableDebugTracing=false e.warnAboutDeprecatedLifecycles=true e.enableProfilerCommitHooks=false e.enableSelectiveHydration=false e.enableBlocksAPI=false e.enableLazyElements=false e.disableJavaScriptURLs=false e.disableInputAttributeSyncing=false e.enableSchedulerDebugging=false e.enableFundamentalAPI=false e.enableScopeAPI=false e.enableCreateEventHandleAPI=false e.warnAboutUnmockedScheduler=false e.enableSuspenseCallback=false e.warnAboutDefaultPropsOnFunctionComponents=false e.warnAboutStringRefs=false e.disableLegacyContext=false e.disableSchedulerTimeoutBasedOnReactExpirationTime=false e.enableTrustedTypesIntegration=false e.disableTextareaChildren=false e.disableModulePatternComponents=false e.warnUnstableRenderSubtreeIntoContainer=false e.warnAboutSpreadingKeyToJSX=false e.enableComponentStackLocations=true e.enableLegacyFBSupport=false e.enableFilterEmptyStringAttributesDOM=false e.skipUnmountedBoundaries=false e.enableNewReconciler=false e.deferRenderPhaseUpdateToNextBatch=true e.enableDiscreteEventFlushingChange=false e.enableEagerRootListeners=true e.enableDoubleInvokingEffects=false k=require("\064\112\107\103\047\064\106\115\100\111\116\108\117\097\047\114\101\097\099\116")X=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")o=require("\064\112\107\103\047\064\106\115\100\111\116\108\117\097\047\114\101\097\099\116-\110\111\111\112-\114\101\110\100\101\114\101\114")K=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")end)S(function()_G.performance=nil end)M("\115\104\111\117\108\100 \110\111\116 \109\097\114\107 \105\102 \101\110\097\098\108\101\083\099\104\101\100\117\108\105\110\103\080\114\111\102\105\108\101\114 \105\115 \102\097\108\115\101",function()X.create(k.createElement("\100\105\118"));(p(h)).toEqual({})end)A("\115\104\111\117\108\100 \108\111\103 \082\101\097\099\116 \118\101\114\115\105\111\110 \111\110 \105\110\105\116\105\097\108\105\122\097\116\105\111\110",function()(p(h)).toEqual({"--\114\101\097\099\116-\105\110\105\116-"..tostring(e)})end)A("\115\104\111\117\108\100 \109\097\114\107 \115\121\110\099 \114\101\110\100\101\114 \119\105\116\104\111\117\116 \115\117\115\112\101\110\100\115 \111\114 \115\116\097\116\101 \117\112\100\097\116\101\115",function()X.create(k.createElement("\100\105\118"));(p(h)).toEqual({"--\114\101\097\099\116-\105\110\105\116-"..tostring(e);"--\115\099\104\101\100\117\108\101-\114\101\110\100\101\114-\049";"--\114\101\110\100\101\114-\115\116\097\114\116-\049","--\114\101\110\100\101\114-\115\116\111\112";"--\099\111\109\109\105\116-\115\116\097\114\116-\049";"--\108\097\121\111\117\116-\101\102\102\101\099\116\115-\115\116\097\114\116-\049","--\108\097\121\111\117\116-\101\102\102\101\099\116\115-\115\116\111\112";"--\099\111\109\109\105\116-\115\116\111\112"})end)A("\115\104\111\117\108\100 \109\097\114\107 \099\111\110\099\117\114\114\101\110\116 \114\101\110\100\101\114 \119\105\116\104\111\117\116 \115\117\115\112\101\110\100\115 \111\114 \115\116\097\116\101 \117\112\100\097\116\101\115",function()X.create(k.createElement("\100\105\118"),{unstable_isConcurrent=true});(p(h)).toEqual({"--\114\101\097\099\116-\105\110\105\116-"..tostring(e);"--\115\099\104\101\100\117\108\101-\114\101\110\100\101\114-\053\049\050"})L.splice(h,1);(p(K)).toFlushUntilNextPaint({});(p(h)).toEqual({"--\114\101\110\100\101\114-\115\116\097\114\116-\053\049\050";"--\114\101\110\100\101\114-\115\116\111\112","--\099\111\109\109\105\116-\115\116\097\114\116-\053\049\050";"--\108\097\121\111\117\116-\101\102\102\101\099\116\115-\115\116\097\114\116-\053\049\050","--\108\097\121\111\117\116-\101\102\102\101\099\116\115-\115\116\111\112";"--\099\111\109\109\105\116-\115\116\111\112"})end)A("\115\104\111\117\108\100 \109\097\114\107 \114\101\110\100\101\114 \121\105\101\108\100\115",function()local function f()K.unstable_yieldValue("\066\097\114")return nil end local function S()K.unstable_yieldValue("\070\111\111")return k.createElement(f)end o.render(k.createElement(S));(p(o.flushNextYield())).toEqual({"\070\111\111"});(p(h)).toEqual({"--\114\101\097\099\116-\105\110\105\116-"..tostring(e);"--\115\099\104\101\100\117\108\101-\114\101\110\100\101\114-\053\049\050";"--\114\101\110\100\101\114-\115\116\097\114\116-\053\049\050","--\114\101\110\100\101\114-\121\105\101\108\100"})end)A("\115\104\111\117\108\100 \109\097\114\107 \115\121\110\099 \114\101\110\100\101\114 \119\105\116\104 \115\117\115\112\101\110\115\101 \116\104\097\116 \114\101\115\111\108\118\101\115",function()local f=(q.delay(0)):andThen(function()return true end)local function S()error(f)end X.create(k.createElement(k.Suspense,{fallback={}},k.createElement(S)));(p(h)).toEqual({"--\114\101\097\099\116-\105\110\105\116-"..tostring(e);"--\115\099\104\101\100\117\108\101-\114\101\110\100\101\114-\049","--\114\101\110\100\101\114-\115\116\097\114\116-\049";"--\115\117\115\112\101\110\115\101-\115\117\115\112\101\110\100-\048-\069\120\097\109\112\108\101","--\114\101\110\100\101\114-\115\116\111\112","--\099\111\109\109\105\116-\115\116\097\114\116-\049";"--\108\097\121\111\117\116-\101\102\102\101\099\116\115-\115\116\097\114\116-\049","--\108\097\121\111\117\116-\101\102\102\101\099\116\115-\115\116\111\112";"--\099\111\109\109\105\116-\115\116\111\112"})L.splice(h,1)f:await();(p(h)).toEqual({"--\115\117\115\112\101\110\115\101-\114\101\115\111\108\118\101\100-\048-\069\120\097\109\112\108\101"})end)A("\115\104\111\117\108\100 \109\097\114\107 \115\121\110\099 \114\101\110\100\101\114 \119\105\116\104 \115\117\115\112\101\110\115\101 \116\104\097\116 \114\101\106\101\099\116\115",function()local f=(q.delay(0)):andThen(function()error(r.new("\101\114\114\111\114"))end)local function S()error(f)end X.create(k.createElement(k.Suspense,{fallback={}},k.createElement(S)));(p(h)).toEqual({"--\114\101\097\099\116-\105\110\105\116-"..tostring(e),"--\115\099\104\101\100\117\108\101-\114\101\110\100\101\114-\049";"--\114\101\110\100\101\114-\115\116\097\114\116-\049","--\115\117\115\112\101\110\115\101-\115\117\115\112\101\110\100-\048-\069\120\097\109\112\108\101","--\114\101\110\100\101\114-\115\116\111\112","--\099\111\109\109\105\116-\115\116\097\114\116-\049";"--\108\097\121\111\117\116-\101\102\102\101\099\116\115-\115\116\097\114\116-\049";"--\108\097\121\111\117\116-\101\102\102\101\099\116\115-\115\116\111\112";"--\099\111\109\109\105\116-\115\116\111\112"})L.splice(h,1);(p(function()f:expect()end)).toThrow();(p(h)).toEqual({"--\115\117\115\112\101\110\115\101-\114\101\106\101\099\116\101\100-\048-\069\120\097\109\112\108\101"})end)A("\115\104\111\117\108\100 \109\097\114\107 \099\111\110\099\117\114\114\101\110\116 \114\101\110\100\101\114 \119\105\116\104 \115\117\115\112\101\110\115\101 \116\104\097\116 \114\101\115\111\108\118\101\115",function()local f=(q.delay(0)):andThen(function()return true end)local function S()error(f)end X.create(k.createElement(k.Suspense,{fallback={}},k.createElement(S)),{unstable_isConcurrent=true});(p(h)).toEqual({"--\114\101\097\099\116-\105\110\105\116-"..tostring(e);"--\115\099\104\101\100\117\108\101-\114\101\110\100\101\114-\053\049\050"})L.splice(h,1);(p(K)).toFlushUntilNextPaint({});(p(h)).toEqual({"--\114\101\110\100\101\114-\115\116\097\114\116-\053\049\050";"--\115\117\115\112\101\110\115\101-\115\117\115\112\101\110\100-\048-\069\120\097\109\112\108\101";"--\114\101\110\100\101\114-\115\116\111\112";"--\099\111\109\109\105\116-\115\116\097\114\116-\053\049\050";"--\108\097\121\111\117\116-\101\102\102\101\099\116\115-\115\116\097\114\116-\053\049\050";"--\108\097\121\111\117\116-\101\102\102\101\099\116\115-\115\116\111\112";"--\099\111\109\109\105\116-\115\116\111\112"})L.splice(h,1)f:expect();(p(h)).toEqual({"--\115\117\115\112\101\110\115\101-\114\101\115\111\108\118\101\100-\048-\069\120\097\109\112\108\101"})end)A("\115\104\111\117\108\100 \109\097\114\107 \099\111\110\099\117\114\114\101\110\116 \114\101\110\100\101\114 \119\105\116\104 \115\117\115\112\101\110\115\101 \116\104\097\116 \114\101\106\101\099\116\115",function()local f=(q.delay(0)):andThen(function()error(r.new("\101\114\114\111\114"))end)local function S()error(f)end X.create(k.createElement(k.Suspense,{fallback={}},k.createElement(S)),{unstable_isConcurrent=true});(p(h)).toEqual({"--\114\101\097\099\116-\105\110\105\116-"..tostring(e);"--\115\099\104\101\100\117\108\101-\114\101\110\100\101\114-\053\049\050"})L.splice(h,1);(p(K)).toFlushUntilNextPaint({});(p(h)).toEqual({"--\114\101\110\100\101\114-\115\116\097\114\116-\053\049\050","--\115\117\115\112\101\110\115\101-\115\117\115\112\101\110\100-\048-\069\120\097\109\112\108\101","--\114\101\110\100\101\114-\115\116\111\112","--\099\111\109\109\105\116-\115\116\097\114\116-\053\049\050","--\108\097\121\111\117\116-\101\102\102\101\099\116\115-\115\116\097\114\116-\053\049\050","--\108\097\121\111\117\116-\101\102\102\101\099\116\115-\115\116\111\112";"--\099\111\109\109\105\116-\115\116\111\112"})L.splice(h,1);(p(function()f:expect()end)).toThrow();(p(h)).toEqual({"--\115\117\115\112\101\110\115\101-\114\101\106\101\099\116\101\100-\048-\069\120\097\109\112\108\101"})end)A("\115\104\111\117\108\100 \109\097\114\107 \099\097\115\099\097\100\105\110\103 \099\108\097\115\115 \099\111\109\112\111\110\101\110\116 \115\116\097\116\101 \117\112\100\097\116\101\115",function()local f=k.Component:extend("\069\120\097\109\112\108\101")function f.init(e)e.state={didMount=false}end function f.componentDidMount(e)e:setState({didMount=true})end function f.render(e)return nil end X.create(k.createElement(f),{unstable_isConcurrent=true});(p(h)).toEqual({"--\114\101\097\099\116-\105\110\105\116-"..tostring(e);"--\115\099\104\101\100\117\108\101-\114\101\110\100\101\114-\053\049\050"})L.splice(h,1);(p(K)).toFlushUntilNextPaint({});(p(h)).toEqual({"--\114\101\110\100\101\114-\115\116\097\114\116-\053\049\050";"--\114\101\110\100\101\114-\115\116\111\112";"--\099\111\109\109\105\116-\115\116\097\114\116-\053\049\050";"--\108\097\121\111\117\116-\101\102\102\101\099\116\115-\115\116\097\114\116-\053\049\050","--\115\099\104\101\100\117\108\101-\115\116\097\116\101-\117\112\100\097\116\101-\049-\069\120\097\109\112\108\101";"--\108\097\121\111\117\116-\101\102\102\101\099\116\115-\115\116\111\112";"--\114\101\110\100\101\114-\115\116\097\114\116-\049","--\114\101\110\100\101\114-\115\116\111\112";"--\099\111\109\109\105\116-\115\116\097\114\116-\049";"--\099\111\109\109\105\116-\115\116\111\112";"--\099\111\109\109\105\116-\115\116\111\112"})end)A("\115\104\111\117\108\100 \109\097\114\107 \099\097\115\099\097\100\105\110\103 \099\108\097\115\115 \099\111\109\112\111\110\101\110\116 \102\111\114\099\101 \117\112\100\097\116\101\115",function()local f=k.Component:extend("\069\120\097\109\112\108\101")function f.componentDidMount(e)e:forceUpdate()end function f.render(e)return nil end X.create(k.createElement(f),{unstable_isConcurrent=true});(p(h)).toEqual({"--\114\101\097\099\116-\105\110\105\116-"..tostring(e);"--\115\099\104\101\100\117\108\101-\114\101\110\100\101\114-\053\049\050"})L.splice(h,1);(p(K)).toFlushUntilNextPaint({});(p(h)).toEqual({"--\114\101\110\100\101\114-\115\116\097\114\116-\053\049\050","--\114\101\110\100\101\114-\115\116\111\112","--\099\111\109\109\105\116-\115\116\097\114\116-\053\049\050","--\108\097\121\111\117\116-\101\102\102\101\099\116\115-\115\116\097\114\116-\053\049\050","--\115\099\104\101\100\117\108\101-\102\111\114\099\101\100-\117\112\100\097\116\101-\049-\069\120\097\109\112\108\101";"--\108\097\121\111\117\116-\101\102\102\101\099\116\115-\115\116\111\112";"--\114\101\110\100\101\114-\115\116\097\114\116-\049";"--\114\101\110\100\101\114-\115\116\111\112","--\099\111\109\109\105\116-\115\116\097\114\116-\049";"--\099\111\109\109\105\116-\115\116\111\112","--\099\111\109\109\105\116-\115\116\111\112"})end)A("\115\104\111\117\108\100 \109\097\114\107 \114\101\110\100\101\114 \112\104\097\115\101 \115\116\097\116\101 \117\112\100\097\116\101\115 \102\111\114 \099\108\097\115\115 \099\111\109\112\111\110\101\110\116",function()local f=k.Component:extend("\069\120\097\109\112\108\101")function f.init(e)e.state={didRender=false}end function f.render(e)if e.state.didRender==false then e:setState({didRender=true})end return nil end X.create(k.createElement(f),{unstable_isConcurrent=true});(p(h)).toEqual({"--\114\101\097\099\116-\105\110\105\116-"..tostring(e);"--\115\099\104\101\100\117\108\101-\114\101\110\100\101\114-\053\049\050"})L.splice(h,1);(p(function()(p(K)).toFlushUntilNextPaint({})end)).toErrorDev("\067\097\110\110\111\116 \117\112\100\097\116\101 \100\117\114\105\110\103 \097\110 \101\120\105\115\116\105\110\103 \115\116\097\116\101 \116\114\097\110\115\105\116\105\111\110");(p(h)).toContain("--\115\099\104\101\100\117\108\101-\115\116\097\116\101-\117\112\100\097\116\101-\049\048\050\052-\069\120\097\109\112\108\101")end)A("\115\104\111\117\108\100 \109\097\114\107 \114\101\110\100\101\114 \112\104\097\115\101 \102\111\114\099\101 \117\112\100\097\116\101\115 \102\111\114 \099\108\097\115\115 \099\111\109\112\111\110\101\110\116",function()local f=k.Component:extend("\069\120\097\109\112\108\101")function f.init(e)e.state={didRender=false}end function f.render(e)if e.state.didRender==false then e:forceUpdate(function()e:setState({didRender=true})end)end return nil end X.create(k.createElement(f),{unstable_isConcurrent=true});(p(h)).toEqual({"--\114\101\097\099\116-\105\110\105\116-"..tostring(e);"--\115\099\104\101\100\117\108\101-\114\101\110\100\101\114-\053\049\050"})L.splice(h,1);(p(function()(p(K)).toFlushUntilNextPaint({})end)).toErrorDev("\067\097\110\110\111\116 \117\112\100\097\116\101 \100\117\114\105\110\103 \097\110 \101\120\105\115\116\105\110\103 \115\116\097\116\101 \116\114\097\110\115\105\116\105\111\110");(p(h)).toContain("--\115\099\104\101\100\117\108\101-\102\111\114\099\101\100-\117\112\100\097\116\101-\049\048\050\052-\069\120\097\109\112\108\101")end)A("\115\104\111\117\108\100 \109\097\114\107 \099\097\115\099\097\100\105\110\103 \108\097\121\111\117\116 \117\112\100\097\116\101\115",function()local function f()local e,p=k.useState(false)k.useLayoutEffect(function()p(true)end,{})return e end X.create(k.createElement(f),{unstable_isConcurrent=true});(p(h)).toEqual({"--\114\101\097\099\116-\105\110\105\116-"..tostring(e);"--\115\099\104\101\100\117\108\101-\114\101\110\100\101\114-\053\049\050"})L.splice(h,1);(p(K)).toFlushUntilNextPaint({});(p(h)).toEqual({"--\114\101\110\100\101\114-\115\116\097\114\116-\053\049\050","--\114\101\110\100\101\114-\115\116\111\112","--\099\111\109\109\105\116-\115\116\097\114\116-\053\049\050";"--\108\097\121\111\117\116-\101\102\102\101\099\116\115-\115\116\097\114\116-\053\049\050";"--\115\099\104\101\100\117\108\101-\115\116\097\116\101-\117\112\100\097\116\101-\049-\069\120\097\109\112\108\101";"--\108\097\121\111\117\116-\101\102\102\101\099\116\115-\115\116\111\112";"--\114\101\110\100\101\114-\115\116\097\114\116-\049";"--\114\101\110\100\101\114-\115\116\111\112";"--\099\111\109\109\105\116-\115\116\097\114\116-\049";"--\099\111\109\109\105\116-\115\116\111\112","--\099\111\109\109\105\116-\115\116\111\112"})end)A("\115\104\111\117\108\100 \109\097\114\107 \099\097\115\099\097\100\105\110\103 \112\097\115\115\105\118\101 \117\112\100\097\116\101\115",function()local function f()local e,p=k.useState(false)k.useEffect(function()p(true)end,{})return e end X.unstable_concurrentAct(function()X.create(k.createElement(f),{unstable_isConcurrent=true})end);(p(h)).toEqual({"--\114\101\097\099\116-\105\110\105\116-"..tostring(e);"--\115\099\104\101\100\117\108\101-\114\101\110\100\101\114-\053\049\050";"--\114\101\110\100\101\114-\115\116\097\114\116-\053\049\050";"--\114\101\110\100\101\114-\115\116\111\112";"--\099\111\109\109\105\116-\115\116\097\114\116-\053\049\050";"--\108\097\121\111\117\116-\101\102\102\101\099\116\115-\115\116\097\114\116-\053\049\050","--\108\097\121\111\117\116-\101\102\102\101\099\116\115-\115\116\111\112";"--\099\111\109\109\105\116-\115\116\111\112";"--\112\097\115\115\105\118\101-\101\102\102\101\099\116\115-\115\116\097\114\116-\053\049\050","--\115\099\104\101\100\117\108\101-\115\116\097\116\101-\117\112\100\097\116\101-\049\048\050\052-\069\120\097\109\112\108\101","--\112\097\115\115\105\118\101-\101\102\102\101\099\116\115-\115\116\111\112","--\114\101\110\100\101\114-\115\116\097\114\116-\049\048\050\052","--\114\101\110\100\101\114-\115\116\111\112";"--\099\111\109\109\105\116-\115\116\097\114\116-\049\048\050\052";"--\099\111\109\109\105\116-\115\116\111\112"})end)A("\115\104\111\117\108\100 \109\097\114\107 \114\101\110\100\101\114 \112\104\097\115\101 \117\112\100\097\116\101\115",function()local function e()local e,p=k.useState(false)if not e then p(true)end return e end X.unstable_concurrentAct(function()X.create(k.createElement(e),{unstable_isConcurrent=true})end);(p(h)).toContain("--\115\099\104\101\100\117\108\101-\115\116\097\116\101-\117\112\100\097\116\101-\049\048\050\052-\069\120\097\109\112\108\101")end)end)
-- ROBLOX upstream: https://github.com/facebook/react/blob/8af27aeedbc6b00bc2ef49729fc84f116c70a27c/packages/react-reconciler/src/__tests__/SchedulingProfiler-test.internal.js
-- /**
--  * Copyright (c) Facebook, Inc. and its affiliates.
--  *
--  * This source code is licensed under the MIT license found in the
--  * LICENSE file in the root directory of this source tree.
--  *
--  * @emails react-core
--  * @jest-environment node
--  */
local ReactVersion = require("@pkg/@jsdotlua/shared").ReactVersion
local JestGlobals = require("@pkg/@jsdotlua/jest-globals")
local jestExpect = JestGlobals.expect
local beforeEach = JestGlobals.beforeEach
local afterEach = JestGlobals.afterEach
local it = JestGlobals.it
local xit = JestGlobals.xit
local jest = JestGlobals.jest
local describe = JestGlobals.describe
local LuauPolyfill = require("@pkg/@jsdotlua/luau-polyfill")
local Array = LuauPolyfill.Array
local Error = LuauPolyfill.Error
local Promise = require("@pkg/@jsdotlua/promise")

describe("SchedulingProfiler", function()
	local React
	local ReactTestRenderer
	local ReactNoop
	local Scheduler

	local marks

	local function createUserTimingPolyfill()
		-- This is not a true polyfill, but it gives us enough to capture marks.
		-- Reference: https://developer.mozilla.org/en-US/docs/Web/API/User_Timing_API
		return {
			mark = function(markName)
				table.insert(marks, markName)
			end,
		}
	end

	beforeEach(function()
		jest.resetModules()
		_G.performance = createUserTimingPolyfill()
		marks = {}

		local ReactFeatureFlags = require("@pkg/@jsdotlua/shared").ReactFeatureFlags
		ReactFeatureFlags.enableSchedulingProfiler = true
		ReactFeatureFlags.enableProfilerTimer = true
		ReactFeatureFlags.replayFailedUnitOfWorkWithInvokeGuardedCallback = true
		ReactFeatureFlags.enableSuspenseServerRenderer = true
		ReactFeatureFlags.decoupleUpdatePriorityFromScheduler = true

		ReactFeatureFlags.debugRenderPhaseSideEffectsForStrictMode = false
		ReactFeatureFlags.enableDebugTracing = false
		ReactFeatureFlags.warnAboutDeprecatedLifecycles = true
		ReactFeatureFlags.enableProfilerCommitHooks = false
		ReactFeatureFlags.enableSelectiveHydration = false
		ReactFeatureFlags.enableBlocksAPI = false
		ReactFeatureFlags.enableLazyElements = false
		ReactFeatureFlags.disableJavaScriptURLs = false
		ReactFeatureFlags.disableInputAttributeSyncing = false
		ReactFeatureFlags.enableSchedulerDebugging = false
		ReactFeatureFlags.enableFundamentalAPI = false
		ReactFeatureFlags.enableScopeAPI = false
		ReactFeatureFlags.enableCreateEventHandleAPI = false
		ReactFeatureFlags.warnAboutUnmockedScheduler = false
		ReactFeatureFlags.enableSuspenseCallback = false
		ReactFeatureFlags.warnAboutDefaultPropsOnFunctionComponents = false
		ReactFeatureFlags.warnAboutStringRefs = false
		ReactFeatureFlags.disableLegacyContext = false
		ReactFeatureFlags.disableSchedulerTimeoutBasedOnReactExpirationTime = false
		ReactFeatureFlags.enableTrustedTypesIntegration = false
		ReactFeatureFlags.disableTextareaChildren = false
		ReactFeatureFlags.disableModulePatternComponents = false
		ReactFeatureFlags.warnUnstableRenderSubtreeIntoContainer = false
		ReactFeatureFlags.warnAboutSpreadingKeyToJSX = false
		ReactFeatureFlags.enableComponentStackLocations = true
		ReactFeatureFlags.enableLegacyFBSupport = false
		ReactFeatureFlags.enableFilterEmptyStringAttributesDOM = false
		ReactFeatureFlags.skipUnmountedBoundaries = false

		ReactFeatureFlags.enableNewReconciler = false
		ReactFeatureFlags.deferRenderPhaseUpdateToNextBatch = true
		ReactFeatureFlags.enableDiscreteEventFlushingChange = false
		ReactFeatureFlags.enableEagerRootListeners = true

		ReactFeatureFlags.enableDoubleInvokingEffects = false

		React = require("@pkg/@jsdotlua/react")

		-- ReactNoop must be imported after ReactTestRenderer!
		ReactTestRenderer = require("@pkg/@jsdotlua/react-test-renderer")
		ReactNoop = require("@pkg/@jsdotlua/react-noop-renderer")

		Scheduler = require("@pkg/@jsdotlua/scheduler")
	end)

	afterEach(function()
		_G.performance = nil
	end)

	-- @gate !enableSchedulingProfiler
	xit("should not mark if enableSchedulingProfiler is false", function()
		ReactTestRenderer.create(React.createElement("div"))
		jestExpect(marks).toEqual({})
	end)

	-- @gate enableSchedulingProfiler
	it("should log React version on initialization", function()
		jestExpect(marks).toEqual({
			"--react-init-" .. tostring(ReactVersion),
		})
	end)

	-- @gate enableSchedulingProfiler
	it("should mark sync render without suspends or state updates", function()
		ReactTestRenderer.create(React.createElement("div"))

		jestExpect(marks).toEqual({
			"--react-init-" .. tostring(ReactVersion),
			"--schedule-render-1",
			"--render-start-1",
			"--render-stop",
			"--commit-start-1",
			"--layout-effects-start-1",
			"--layout-effects-stop",
			"--commit-stop",
		})
	end)

	-- @gate enableSchedulingProfiler
	it("should mark concurrent render without suspends or state updates", function()
		ReactTestRenderer.create(
			React.createElement("div"),
			{ unstable_isConcurrent = true }
		)

		jestExpect(marks).toEqual({
			"--react-init-" .. tostring(ReactVersion),
			"--schedule-render-512",
		})

		Array.splice(marks, 1)

		jestExpect(Scheduler).toFlushUntilNextPaint({})

		jestExpect(marks).toEqual({
			"--render-start-512",
			"--render-stop",
			"--commit-start-512",
			"--layout-effects-start-512",
			"--layout-effects-stop",
			"--commit-stop",
		})
	end)

	-- @gate enableSchedulingProfiler
	it("should mark render yields", function()
		local function Bar()
			Scheduler.unstable_yieldValue("Bar")
			return nil
		end

		local function Foo()
			Scheduler.unstable_yieldValue("Foo")
			return React.createElement(Bar)
		end

		ReactNoop.render(React.createElement(Foo))
		-- Do one step of work.
		jestExpect(ReactNoop.flushNextYield()).toEqual({ "Foo" })

		jestExpect(marks).toEqual({
			"--react-init-" .. tostring(ReactVersion),
			"--schedule-render-512",
			"--render-start-512",
			"--render-yield",
		})
	end)

	-- @gate enableSchedulingProfiler
	it("should mark sync render with suspense that resolves", function()
		-- ROBLOX deviation: can't just Promise.resolve() due to evaera Promise issue
		local fakeSuspensePromise = Promise.delay(0):andThen(function()
			return true
		end)
		local function Example()
			error(fakeSuspensePromise)
		end

		ReactTestRenderer.create(React.createElement(
			React.Suspense,
			-- ROBLOX deviation: Lua can't express 'empty' fallback with nil, so we use empty array
			{ fallback = {} },
			React.createElement(Example)
		))

		jestExpect(marks).toEqual({
			"--react-init-" .. tostring(ReactVersion),
			"--schedule-render-1",
			"--render-start-1",
			"--suspense-suspend-0-Example",
			"--render-stop",
			"--commit-start-1",
			"--layout-effects-start-1",
			"--layout-effects-stop",
			"--commit-stop",
		})

		Array.splice(marks, 1)

		fakeSuspensePromise:await()
		jestExpect(marks).toEqual({ "--suspense-resolved-0-Example" })
	end)

	-- @gate enableSchedulingProfiler
	it("should mark sync render with suspense that rejects", function()
		-- ROBLOX deviation: can't just Promise.reject() due to evaera Promise issue
		local fakeSuspensePromise = Promise.delay(0):andThen(function()
			error(Error.new("error"))
		end)
		local function Example()
			error(fakeSuspensePromise)
		end

		ReactTestRenderer.create(React.createElement(
			React.Suspense,
			-- ROBLOX deviation: Lua can't express 'empty' fallback with nil, so we use empty array
			{ fallback = {} },
			React.createElement(Example)
		))

		jestExpect(marks).toEqual({
			"--react-init-" .. tostring(ReactVersion),
			"--schedule-render-1",
			"--render-start-1",
			"--suspense-suspend-0-Example",
			"--render-stop",
			"--commit-start-1",
			"--layout-effects-start-1",
			"--layout-effects-stop",
			"--commit-stop",
		})

		Array.splice(marks, 1)

		-- ROBLOX deviation: jest-roblox doesn't support these Promise matchers yet
		-- await jestExpect(fakeSuspensePromise).rejects.toThrow()
		jestExpect(function()
			fakeSuspensePromise:expect()
		end).toThrow()

		jestExpect(marks).toEqual({ "--suspense-rejected-0-Example" })
	end)

	-- @gate enableSchedulingProfiler
	it("should mark concurrent render with suspense that resolves", function()
		-- ROBLOX deviation: can't just Promise.resolve() due to evaera Promise issue
		local fakeSuspensePromise = Promise.delay(0):andThen(function()
			return true
		end)
		local function Example()
			error(fakeSuspensePromise)
		end

		ReactTestRenderer.create(
			React.createElement(
				React.Suspense,
				-- ROBLOX deviation: Lua can't express 'empty' fallback with nil, so we use empty array
				{ fallback = {} },
				React.createElement(Example)
			),
			{ unstable_isConcurrent = true }
		)

		jestExpect(marks).toEqual({
			"--react-init-" .. tostring(ReactVersion),
			"--schedule-render-512",
		})

		Array.splice(marks, 1)

		jestExpect(Scheduler).toFlushUntilNextPaint({})

		jestExpect(marks).toEqual({
			"--render-start-512",
			"--suspense-suspend-0-Example",
			"--render-stop",
			"--commit-start-512",
			"--layout-effects-start-512",
			"--layout-effects-stop",
			"--commit-stop",
		})

		Array.splice(marks, 1)

		fakeSuspensePromise:expect()

		jestExpect(marks).toEqual({ "--suspense-resolved-0-Example" })
	end)

	-- @gate enableSchedulingProfiler
	it("should mark concurrent render with suspense that rejects", function()
		-- ROBLOX deviation: can't just Promise.reject() due to evaera Promise issue
		local fakeSuspensePromise = Promise.delay(0):andThen(function()
			error(Error.new("error"))
		end)

		local function Example()
			error(fakeSuspensePromise)
		end

		ReactTestRenderer.create(
			React.createElement(
				React.Suspense,
				-- ROBLOX deviation: Lua can't express 'empty' fallback with nil, so we use empty array
				{ fallback = {} },
				React.createElement(Example)
			),
			{ unstable_isConcurrent = true }
		)

		jestExpect(marks).toEqual({
			"--react-init-" .. tostring(ReactVersion),
			"--schedule-render-512",
		})

		Array.splice(marks, 1)

		jestExpect(Scheduler).toFlushUntilNextPaint({})

		jestExpect(marks).toEqual({
			"--render-start-512",
			"--suspense-suspend-0-Example",
			"--render-stop",
			"--commit-start-512",
			"--layout-effects-start-512",
			"--layout-effects-stop",
			"--commit-stop",
		})

		Array.splice(marks, 1)

		-- ROBLOX deviation: jest-roblox doesn't support these Promise matchers yet
		-- await jestExpect(fakeSuspensePromise).rejects.toThrow()
		jestExpect(function()
			fakeSuspensePromise:expect()
		end).toThrow()
		jestExpect(marks).toEqual({ "--suspense-rejected-0-Example" })
	end)

	-- @gate enableSchedulingProfiler
	it("should mark cascading class component state updates", function()
		local Example = React.Component:extend("Example")
		function Example:init()
			self.state = { didMount = false }
		end
		function Example:componentDidMount()
			self:setState({ didMount = true })
		end
		function Example:render()
			return nil
		end

		ReactTestRenderer.create(
			React.createElement(Example),
			{ unstable_isConcurrent = true }
		)

		jestExpect(marks).toEqual({
			"--react-init-" .. tostring(ReactVersion),
			"--schedule-render-512",
		})

		Array.splice(marks, 1)

		jestExpect(Scheduler).toFlushUntilNextPaint({})

		jestExpect(marks).toEqual({
			"--render-start-512",
			"--render-stop",
			"--commit-start-512",
			"--layout-effects-start-512",
			"--schedule-state-update-1-Example",
			"--layout-effects-stop",
			"--render-start-1",
			"--render-stop",
			"--commit-start-1",
			"--commit-stop",
			"--commit-stop",
		})
	end)

	-- @gate enableSchedulingProfiler
	it("should mark cascading class component force updates", function()
		local Example = React.Component:extend("Example")
		function Example:componentDidMount()
			self:forceUpdate()
		end
		function Example:render()
			return nil
		end

		ReactTestRenderer.create(
			React.createElement(Example),
			{ unstable_isConcurrent = true }
		)

		jestExpect(marks).toEqual({
			"--react-init-" .. tostring(ReactVersion),
			"--schedule-render-512",
		})

		Array.splice(marks, 1)

		jestExpect(Scheduler).toFlushUntilNextPaint({})

		jestExpect(marks).toEqual({
			"--render-start-512",
			"--render-stop",
			"--commit-start-512",
			"--layout-effects-start-512",
			"--schedule-forced-update-1-Example",
			"--layout-effects-stop",
			"--render-start-1",
			"--render-stop",
			"--commit-start-1",
			"--commit-stop",
			"--commit-stop",
		})
	end)

	-- @gate enableSchedulingProfiler
	it("should mark render phase state updates for class component", function()
		local Example = React.Component:extend("Example")
		function Example:init()
			self.state = { didRender = false }
		end
		function Example:render()
			if self.state.didRender == false then
				self:setState({ didRender = true })
			end
			return nil
		end

		ReactTestRenderer.create(
			React.createElement(Example),
			{ unstable_isConcurrent = true }
		)

		jestExpect(marks).toEqual({
			"--react-init-" .. tostring(ReactVersion),
			"--schedule-render-512",
		})

		Array.splice(marks, 1)

		jestExpect(function()
			jestExpect(Scheduler).toFlushUntilNextPaint({})
		end).toErrorDev("Cannot update during an existing state transition")

		-- ROBLOX FIXME: no way to gate feature tests like upstream
		-- gate(({old}) =>
		--   old
		jestExpect(marks).toContain("--schedule-state-update-1024-Example")
		-- jestExpect(marks).toContain('--schedule-state-update-512-Example')
		-- )
	end)

	-- @gate enableSchedulingProfiler
	it("should mark render phase force updates for class component", function()
		local Example = React.Component:extend("Example")
		function Example:init()
			self.state = { didRender = false }
		end
		function Example:render()
			if self.state.didRender == false then
				self:forceUpdate(function()
					self:setState({ didRender = true })
				end)
			end
			return nil
		end

		ReactTestRenderer.create(
			React.createElement(Example),
			{ unstable_isConcurrent = true }
		)

		jestExpect(marks).toEqual({
			"--react-init-" .. tostring(ReactVersion),
			"--schedule-render-512",
		})

		Array.splice(marks, 1)

		jestExpect(function()
			jestExpect(Scheduler).toFlushUntilNextPaint({})
		end).toErrorDev("Cannot update during an existing state transition")

		-- ROBLOX TODO: we have no way to gate tests on features like upstream
		-- gate(({old}) =>
		--   old
		jestExpect(marks).toContain("--schedule-forced-update-1024-Example")
		-- jestExpect(marks).toContain('--schedule-forced-update-512-Example')
		-- )
	end)

	-- @gate enableSchedulingProfiler
	it("should mark cascading layout updates", function()
		local function Example()
			local didMount, setDidMount = React.useState(false)
			React.useLayoutEffect(function()
				setDidMount(true)
			end, {})
			return didMount
		end

		ReactTestRenderer.create(
			React.createElement(Example),
			{ unstable_isConcurrent = true }
		)

		jestExpect(marks).toEqual({
			"--react-init-" .. tostring(ReactVersion),
			"--schedule-render-512",
		})

		Array.splice(marks, 1)

		jestExpect(Scheduler).toFlushUntilNextPaint({})

		jestExpect(marks).toEqual({
			"--render-start-512",
			"--render-stop",
			"--commit-start-512",
			"--layout-effects-start-512",
			"--schedule-state-update-1-Example",
			"--layout-effects-stop",
			"--render-start-1",
			"--render-stop",
			"--commit-start-1",
			"--commit-stop",
			"--commit-stop",
		})
	end)

	-- @gate enableSchedulingProfiler
	it("should mark cascading passive updates", function()
		local function Example()
			local didMount, setDidMount = React.useState(false)
			React.useEffect(function()
				setDidMount(true)
			end, {})
			return didMount
		end

		ReactTestRenderer.unstable_concurrentAct(function()
			ReactTestRenderer.create(
				React.createElement(Example),
				{ unstable_isConcurrent = true }
			)
		end)

		jestExpect(marks).toEqual({
			"--react-init-" .. tostring(ReactVersion),
			"--schedule-render-512",
			"--render-start-512",
			"--render-stop",
			"--commit-start-512",
			"--layout-effects-start-512",
			"--layout-effects-stop",
			"--commit-stop",
			"--passive-effects-start-512",
			"--schedule-state-update-1024-Example",
			"--passive-effects-stop",
			"--render-start-1024",
			"--render-stop",
			"--commit-start-1024",
			"--commit-stop",
		})
	end)

	-- @gate enableSchedulingProfiler
	it("should mark render phase updates", function()
		local function Example()
			local didRender, setDidRender = React.useState(false)
			if not didRender then
				setDidRender(true)
			end
			return didRender
		end

		ReactTestRenderer.unstable_concurrentAct(function()
			ReactTestRenderer.create(
				React.createElement(Example),
				{ unstable_isConcurrent = true }
			)
		end)

		-- ROBLOX TODO: we don't have a way to gate tests based on features like upstream does
		-- gate(({old}) =>
		--   old
		jestExpect(marks).toContain("--schedule-state-update-1024-Example")
		--     : jestExpect(marks).toContain('--schedule-state-update-512-Example'),
		-- )
	end)
end)
