function TestRunner.runPlanNode(U,c,B)local function z(c,B)local z=true local n _G[RUNNING_GLOBAL]=true B=B or""local r=getfenv(c)for U,c in pairs(TestRunner.environment)do r[U]=c end r.fail=function(U)if U==nil then U="\102\097\105\108\040\041 \119\097\115 \099\097\108\108\101\100\046"end z=false n=B..debug.traceback(tostring(U),2)end r.expect=wrapExpectContextWithPublicApi(U:getExpectationContext())local A=U:getContext()local function N(U)U=U:split("\n")local c={}for U,B in pairs(U)do if B:match("\084\101\115\116\069\090\037\046\084\101\115\116\069\090\037\046")then break end table.insert(c,B)end return table.concat(c,"\n")end local K,l=xpcall(function()c(A)end,function(U)if typeof(U)=="\116\097\098\108\101"and(U.stack and(U.name and U.message))then return B..N(tostring(U)..("\n"..U.stack))else return B..debug.traceback(tostring(U),2)end end)if not K then z=false n=l end _G[RUNNING_GLOBAL]=nil return z,n end local function n(U)for U,c in ipairs(B:getBeforeEachHooks())do local B,n=z(c,"\098\101\102\111\114\101\069\097\099\104 \104\111\111\107\058 ")if not B then return false,n end end local c,n=z(U.callback)for U,B in ipairs(B:getAfterEachHooks())do local r,A=z(B,"\097\102\116\101\114\069\097\099\104 \104\111\111\107\058 ")if not r then if not c then return false,n..("\n\087\104\105\108\101 \099\108\101\097\110\105\110\103 \117\112 \116\104\101 \102\097\105\108\101\100 \116\101\115\116 \097\110\111\116\104\101\114 \101\114\114\111\114 \119\097\115 \102\111\117\110\100\058\n"..A)end return false,A end end if not c then return false,n end return true,nil end B:pushHooksFrom(c)local r=false for c,B in ipairs(B:getBeforeAllHooks())do local n,A=z(B,"\098\101\102\111\114\101\065\108\108 \104\111\111\107\058 ")if not n then U:addDummyError("\098\101\102\111\114\101\065\108\108",A)r=true end end if c.isRoot then _G[JEST_TEST_CONTEXT].instance=c.instance _G[JEST_TEST_CONTEXT].blocks={}_G[JEST_TEST_CONTEXT].snapshotState=nil end if not r then for c,z in ipairs(c.children)do table.insert(_G[JEST_TEST_CONTEXT].blocks,z.phrase)if z.type==TestEnum.NodeType.It then U:pushNode(z)if U:shouldSkip()then U:setSkipped()else local c,B=n(z)if c then U:setSuccess()else U:setError(B)end end U:popNode()elseif z.type==TestEnum.NodeType.Describe then U:pushNode(z)TestRunner.runPlanNode(U,z,B)if z.loadError then local c="\069\114\114\111\114 \100\117\114\105\110\103 \112\108\097\110\110\105\110\103\058 "..z.loadError U:setError(c)else U:setStatusFromChildren()end U:popNode()end table.remove(_G[JEST_TEST_CONTEXT].blocks)end end local A=_G[JEST_TEST_CONTEXT].snapshotState if c.isRoot and(A and A._updateSnapshot~="\110\111\110\101")then local U=A:getUncheckedCount()if U>0 then A:removeUncheckedKeys()end A:save()end for c,B in ipairs(B:getAfterAllHooks())do local n,r=z(B,"\097\102\116\101\114\065\108\108 \104\111\111\107\058 ")if not n then U:addDummyError("\097\102\116\101\114\065\108\108",r)end end B:popHooks()end return TestRunner

function TestRunner.runPlanNode(session, planNode, lifecycleHooks)
	local function runCallback(callback, messagePrefix)
		local success = true
		local errorMessage
		-- Any code can check RUNNING_GLOBAL to fork behavior based on
		-- whether a test is running. We use this to avoid accessing
		-- protected APIs; it's a workaround that will go away someday.
		_G[RUNNING_GLOBAL] = true

		messagePrefix = messagePrefix or ""

		local testEnvironment = getfenv(callback)

		for key, value in pairs(TestRunner.environment) do
			testEnvironment[key] = value
		end

		testEnvironment.fail = function(message)
			if message == nil then
				message = "fail() was called."
			end

			success = false
			errorMessage = messagePrefix .. debug.traceback(tostring(message), 2)
		end

		testEnvironment.expect = wrapExpectContextWithPublicApi(session:getExpectationContext())

		local context = session:getContext()

		local function removeTestEZFromStack(stack)
			stack = stack:split("\n")
			local lines = {}
			for _, line in pairs(stack) do
				if line:match("TestEZ%.TestEZ%.") then
					break
				end
				table.insert(lines, line)
			end
			return table.concat(lines, "\n")
		end

		local nodeSuccess, nodeResult = xpcall(function()
			callback(context)
		end, function(message)
			if typeof(message) == "table" and message.stack and message.name and message.message then
				return messagePrefix .. removeTestEZFromStack(tostring(message) .. "\n" .. message.stack)
			else
				return messagePrefix .. debug.traceback(tostring(message), 2)
			end
		end)

		-- If a node threw an error, we prefer to use that message over
		-- one created by fail() if it was set.
		if not nodeSuccess then
			success = false
			errorMessage = nodeResult
		end

		_G[RUNNING_GLOBAL] = nil

		return success, errorMessage
	end

	local function runNode(childPlanNode)
		-- Errors can be set either via `error` propagating upwards or
		-- by a test calling fail([message]).
		for _, hook in ipairs(lifecycleHooks:getBeforeEachHooks()) do
			local success, errorMessage = runCallback(hook, "beforeEach hook: ")
			if not success then
				return false, errorMessage
			end
		end

		local testSuccess, testErrorMessage = runCallback(childPlanNode.callback)

		for _, hook in ipairs(lifecycleHooks:getAfterEachHooks()) do
			local success, errorMessage = runCallback(hook, "afterEach hook: ")
			if not success then
				if not testSuccess then
					return false,
						testErrorMessage
							.. "\nWhile cleaning up the failed test another error was found:\n"
							.. errorMessage
				end
				return false, errorMessage
			end
		end

		if not testSuccess then
			return false, testErrorMessage
		end

		return true, nil
	end

	lifecycleHooks:pushHooksFrom(planNode)

	local halt = false
	for _, hook in ipairs(lifecycleHooks:getBeforeAllHooks()) do
		local success, errorMessage = runCallback(hook, "beforeAll hook: ")
		if not success then
			session:addDummyError("beforeAll", errorMessage)
			halt = true
		end
	end

	-- Reset the jest test context every time we process a node that
	-- corresponds to a new spec file
	if planNode.isRoot then
		_G[JEST_TEST_CONTEXT].instance = planNode.instance
		_G[JEST_TEST_CONTEXT].blocks = {}
		_G[JEST_TEST_CONTEXT].snapshotState = nil
	end

	if not halt then
		for _, childPlanNode in ipairs(planNode.children) do
			table.insert(_G[JEST_TEST_CONTEXT].blocks, childPlanNode.phrase)
			if childPlanNode.type == TestEnum.NodeType.It then
				session:pushNode(childPlanNode)
				if session:shouldSkip() then
					session:setSkipped()
				else
					local success, errorMessage = runNode(childPlanNode)

					if success then
						session:setSuccess()
					else
						session:setError(errorMessage)
					end
				end
				session:popNode()
			elseif childPlanNode.type == TestEnum.NodeType.Describe then
				session:pushNode(childPlanNode)
				TestRunner.runPlanNode(session, childPlanNode, lifecycleHooks)

				-- Did we have an error trying build a test plan?
				if childPlanNode.loadError then
					local message = "Error during planning: " .. childPlanNode.loadError
					session:setError(message)
				else
					session:setStatusFromChildren()
				end
				session:popNode()
			end
			table.remove(_G[JEST_TEST_CONTEXT].blocks)
		end
	end

	local snapshotState = _G[JEST_TEST_CONTEXT].snapshotState
	if planNode.isRoot and snapshotState and snapshotState._updateSnapshot ~= "none" then
		local uncheckedCount = snapshotState:getUncheckedCount()
		if uncheckedCount > 0 then
			snapshotState:removeUncheckedKeys()
		end
		snapshotState:save()
	end

	for _, hook in ipairs(lifecycleHooks:getAfterAllHooks()) do
		local success, errorMessage = runCallback(hook, "afterAll hook: ")
		if not success then
			session:addDummyError("afterAll", errorMessage)
		end
	end

	lifecycleHooks:popHooks()
end

return TestRunner
