function TestNode.new(Q,x,n,Z,N)Z=Z or TestEnum.NodeModifier.None local g={plan=Q,phrase=x,type=n,modifier=Z,children={},callback=nil,parent=nil}g.environment=newEnvironment(g,Q.extraEnvironment,N)return setmetatable(g,TestNode)end local function Q(Q,x,n)if x and(n==nil or n==TestEnum.NodeModifier.None)then if Q:match(x)then return TestEnum.NodeModifier.Focus else return TestEnum.NodeModifier.Skip end end return n end local function x(Q,x,n)if x and(n==nil or n==TestEnum.NodeModifier.None)then if Q:match(x)then return TestEnum.NodeModifier.Skip end end return n end function TestNode.addChild(T,n,Z,N,g)if Z==TestEnum.NodeType.It then for Q,x in pairs(T.children)do if x.phrase==n then error("\068\117\112\108\105\099\097\116\101 \105\116 \098\108\111\099\107 \102\111\117\110\100\058 "..x:getFullName())end end end local y=T:getFullName()..(" "..n)local V={}if T.plan.testNamePattern then V.nameNodeModifier=Q(y,T.plan.testNamePattern,N)end if T.plan.testPathPattern and g then V.pathNodeModifier=Q(g,T.plan.testPathPattern,N)end if T.plan.testPathIgnorePatterns and g then V.pathIgnoreNodeModifier=x(g,T.plan.testPathIgnorePatterns,N)end for Q,x in pairs(V)do if x~=TestEnum.NodeModifier.Focus then N=x break else N=x end end if T.plan.runTestsByPath and(#T.plan.runTestsByPath>0 and g)then if not table.find(T.plan.runTestsByPath,g)then N=TestEnum.NodeModifier.Skip end end local W=TestNode.new(T.plan,n,Z,N,g)W.parent=T table.insert(T.children,W)return W end

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
