function TestNode.new(M,O,y,K,e)K=K or TestEnum.NodeModifier.None local d={plan=M;phrase=O;type=y;modifier=K;children={};callback=nil;parent=nil}d.environment=newEnvironment(d,M.extraEnvironment,e)return setmetatable(d,TestNode)end local function M(M,O,y)if O and(y==nil or y==TestEnum.NodeModifier.None)then if M:match(O)then return TestEnum.NodeModifier.Focus else return TestEnum.NodeModifier.Skip end end return y end local function O(M,O,y)if O and(y==nil or y==TestEnum.NodeModifier.None)then if M:match(O)then return TestEnum.NodeModifier.Skip end end return y end function TestNode.addChild(P,y,K,e,d)if K==TestEnum.NodeType.It then for M,O in pairs(P.children)do if O.phrase==y then error("\068\117\112\108\105\099\097\116\101 \105\116 \098\108\111\099\107 \102\111\117\110\100\058 "..O:getFullName())end end end local h=P:getFullName()..(" "..y)local o={}if P.plan.testNamePattern then o.nameNodeModifier=M(h,P.plan.testNamePattern,e)end if P.plan.testPathPattern and d then o.pathNodeModifier=M(d,P.plan.testPathPattern,e)end if P.plan.testPathIgnorePatterns and d then o.pathIgnoreNodeModifier=O(d,P.plan.testPathIgnorePatterns,e)end for M,O in pairs(o)do if O~=TestEnum.NodeModifier.Focus then e=O break else e=O end end if P.plan.runTestsByPath and(#P.plan.runTestsByPath>0 and d)then if not table.find(P.plan.runTestsByPath,d)then e=TestEnum.NodeModifier.Skip end end local i=TestNode.new(P.plan,y,K,e,d)i.parent=P table.insert(P.children,i)return i end
--[[
	Create a new test node. A pointer to the test plan, a phrase to describe it
	and the type of node it is are required. The modifier is optional and will
	be None if left blank.
]]
function TestNode.new(plan, phrase, nodeType, nodeModifier, filePath)
	nodeModifier = nodeModifier or TestEnum.NodeModifier.None

	local node = {
		plan = plan,
		phrase = phrase,
		type = nodeType,
		modifier = nodeModifier,
		children = {},
		callback = nil,
		parent = nil,
	}

	node.environment = newEnvironment(node, plan.extraEnvironment, filePath)
	return setmetatable(node, TestNode)
end

local function getModifier(name, pattern, modifier)
	if pattern and (modifier == nil or modifier == TestEnum.NodeModifier.None) then
		if name:match(pattern) then
			return TestEnum.NodeModifier.Focus
		else
			return TestEnum.NodeModifier.Skip
		end
	end
	return modifier
end

local function getIgnoreModifier(name, pattern, modifier)
	if pattern and (modifier == nil or modifier == TestEnum.NodeModifier.None) then
		if name:match(pattern) then
			return TestEnum.NodeModifier.Skip
		end
	end
	return modifier
end

function TestNode:addChild(phrase, nodeType, nodeModifier, filePath)
	if nodeType == TestEnum.NodeType.It then
		for _, child in pairs(self.children) do
			if child.phrase == phrase then
				error("Duplicate it block found: " .. child:getFullName())
			end
		end
	end

	local childName = self:getFullName() .. " " .. phrase
	local filters = {}
	if self.plan.testNamePattern then
		filters["nameNodeModifier"] = getModifier(childName, self.plan.testNamePattern, nodeModifier)
	end
	if self.plan.testPathPattern and filePath then
		filters["pathNodeModifier"] = getModifier(filePath, self.plan.testPathPattern, nodeModifier)
	end
	if self.plan.testPathIgnorePatterns and filePath then
		filters["pathIgnoreNodeModifier"] = getIgnoreModifier(filePath, self.plan.testPathIgnorePatterns, nodeModifier)
	end

	for _, filter in pairs(filters) do
		if filter ~= TestEnum.NodeModifier.Focus then
			nodeModifier = filter
			break
		else
			nodeModifier = filter
		end
	end

	if self.plan.runTestsByPath and #self.plan.runTestsByPath > 0 and filePath then
		if not table.find(self.plan.runTestsByPath, filePath) then
			nodeModifier = TestEnum.NodeModifier.Skip
		end
	end

	local child = TestNode.new(self.plan, phrase, nodeType, nodeModifier, filePath)
	child.parent = self
	table.insert(self.children, child)
	return child
end
