You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							285 lines
						
					
					
						
							7.0 KiB
						
					
					
				
			
		
		
	
	
							285 lines
						
					
					
						
							7.0 KiB
						
					
					
				/* | 
						|
 Copyright 2012-2015, Yahoo Inc. | 
						|
 Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. | 
						|
 */ | 
						|
'use strict'; | 
						|
 | 
						|
const util = require('util'); | 
						|
const coverage = require('istanbul-lib-coverage'); | 
						|
const Path = require('./path'); | 
						|
const tree = require('./tree'); | 
						|
const BaseNode = tree.Node; | 
						|
const BaseTree = tree.Tree; | 
						|
 | 
						|
function ReportNode(path, fileCoverage) { | 
						|
    this.path = path; | 
						|
    this.parent = null; | 
						|
    this.fileCoverage = fileCoverage; | 
						|
    this.children = []; | 
						|
} | 
						|
 | 
						|
util.inherits(ReportNode, BaseNode); | 
						|
 | 
						|
ReportNode.prototype.addChild = function(child) { | 
						|
    child.parent = this; | 
						|
    this.children.push(child); | 
						|
}; | 
						|
 | 
						|
ReportNode.prototype.asRelative = function(p) { | 
						|
    /* istanbul ignore if */ | 
						|
    if (p.substring(0, 1) === '/') { | 
						|
        return p.substring(1); | 
						|
    } | 
						|
    return p; | 
						|
}; | 
						|
 | 
						|
ReportNode.prototype.getQualifiedName = function() { | 
						|
    return this.asRelative(this.path.toString()); | 
						|
}; | 
						|
 | 
						|
ReportNode.prototype.getRelativeName = function() { | 
						|
    const parent = this.getParent(); | 
						|
    const myPath = this.path; | 
						|
    let relPath; | 
						|
    let i; | 
						|
    const parentPath = parent ? parent.path : new Path([]); | 
						|
    if (parentPath.ancestorOf(myPath)) { | 
						|
        relPath = new Path(myPath.elements()); | 
						|
        for (i = 0; i < parentPath.length; i += 1) { | 
						|
            relPath.shift(); | 
						|
        } | 
						|
        return this.asRelative(relPath.toString()); | 
						|
    } | 
						|
    return this.asRelative(this.path.toString()); | 
						|
}; | 
						|
 | 
						|
ReportNode.prototype.getParent = function() { | 
						|
    return this.parent; | 
						|
}; | 
						|
 | 
						|
ReportNode.prototype.getChildren = function() { | 
						|
    return this.children; | 
						|
}; | 
						|
 | 
						|
ReportNode.prototype.isSummary = function() { | 
						|
    return !this.fileCoverage; | 
						|
}; | 
						|
 | 
						|
ReportNode.prototype.getFileCoverage = function() { | 
						|
    return this.fileCoverage; | 
						|
}; | 
						|
 | 
						|
ReportNode.prototype.getCoverageSummary = function(filesOnly) { | 
						|
    const cacheProp = 'c_' + (filesOnly ? 'files' : 'full'); | 
						|
    let summary; | 
						|
 | 
						|
    if (this.hasOwnProperty(cacheProp)) { | 
						|
        return this[cacheProp]; | 
						|
    } | 
						|
 | 
						|
    if (!this.isSummary()) { | 
						|
        summary = this.getFileCoverage().toSummary(); | 
						|
    } else { | 
						|
        let count = 0; | 
						|
        summary = coverage.createCoverageSummary(); | 
						|
        this.getChildren().forEach(child => { | 
						|
            if (filesOnly && child.isSummary()) { | 
						|
                return; | 
						|
            } | 
						|
            count += 1; | 
						|
            summary.merge(child.getCoverageSummary(filesOnly)); | 
						|
        }); | 
						|
        if (count === 0 && filesOnly) { | 
						|
            summary = null; | 
						|
        } | 
						|
    } | 
						|
    this[cacheProp] = summary; | 
						|
    return summary; | 
						|
}; | 
						|
 | 
						|
function treeFor(root, childPrefix) { | 
						|
    const tree = new BaseTree(); | 
						|
    const maybePrefix = function(node) { | 
						|
        if (childPrefix && !node.isRoot()) { | 
						|
            node.path.unshift(childPrefix); | 
						|
        } | 
						|
    }; | 
						|
    tree.getRoot = function() { | 
						|
        return root; | 
						|
    }; | 
						|
    const visitor = { | 
						|
        onDetail(node) { | 
						|
            maybePrefix(node); | 
						|
        }, | 
						|
        onSummary(node) { | 
						|
            maybePrefix(node); | 
						|
            node.children.sort((a, b) => { | 
						|
                const astr = a.path.toString(); | 
						|
                const bstr = b.path.toString(); | 
						|
                return astr < bstr | 
						|
                    ? -1 | 
						|
                    : astr > bstr | 
						|
                    ? 1 | 
						|
                    : /* istanbul ignore next */ 0; | 
						|
            }); | 
						|
        } | 
						|
    }; | 
						|
    tree.visit(visitor); | 
						|
    return tree; | 
						|
} | 
						|
 | 
						|
function findCommonParent(paths) { | 
						|
    if (paths.length === 0) { | 
						|
        return new Path([]); | 
						|
    } | 
						|
    let common = paths[0]; | 
						|
    let i; | 
						|
 | 
						|
    for (i = 1; i < paths.length; i += 1) { | 
						|
        common = common.commonPrefixPath(paths[i]); | 
						|
        if (common.length === 0) { | 
						|
            break; | 
						|
        } | 
						|
    } | 
						|
    return common; | 
						|
} | 
						|
 | 
						|
function toInitialList(coverageMap) { | 
						|
    const ret = []; | 
						|
    coverageMap.files().forEach(filePath => { | 
						|
        const p = new Path(filePath); | 
						|
        const coverage = coverageMap.fileCoverageFor(filePath); | 
						|
        ret.push({ | 
						|
            filePath, | 
						|
            path: p, | 
						|
            fileCoverage: coverage | 
						|
        }); | 
						|
    }); | 
						|
 | 
						|
    const commonParent = findCommonParent(ret.map(o => o.path.parent())); | 
						|
    if (commonParent.length > 0) { | 
						|
        ret.forEach(o => { | 
						|
            o.path.splice(0, commonParent.length); | 
						|
        }); | 
						|
    } | 
						|
    return { | 
						|
        list: ret, | 
						|
        commonParent | 
						|
    }; | 
						|
} | 
						|
 | 
						|
function toDirParents(list) { | 
						|
    const nodeMap = Object.create(null); | 
						|
    const parentNodeList = []; | 
						|
    list.forEach(o => { | 
						|
        const node = new ReportNode(o.path, o.fileCoverage); | 
						|
        const parentPath = o.path.parent(); | 
						|
        let parent = nodeMap[parentPath.toString()]; | 
						|
 | 
						|
        if (!parent) { | 
						|
            parent = new ReportNode(parentPath); | 
						|
            nodeMap[parentPath.toString()] = parent; | 
						|
            parentNodeList.push(parent); | 
						|
        } | 
						|
        parent.addChild(node); | 
						|
    }); | 
						|
    return parentNodeList; | 
						|
} | 
						|
 | 
						|
function foldIntoParents(nodeList) { | 
						|
    const ret = []; | 
						|
    let i; | 
						|
    let j; | 
						|
 | 
						|
    // sort by longest length first | 
						|
    nodeList.sort((a, b) => -1 * Path.compare(a.path, b.path)); | 
						|
 | 
						|
    for (i = 0; i < nodeList.length; i += 1) { | 
						|
        const first = nodeList[i]; | 
						|
        let inserted = false; | 
						|
 | 
						|
        for (j = i + 1; j < nodeList.length; j += 1) { | 
						|
            const second = nodeList[j]; | 
						|
            if (second.path.ancestorOf(first.path)) { | 
						|
                second.addChild(first); | 
						|
                inserted = true; | 
						|
                break; | 
						|
            } | 
						|
        } | 
						|
 | 
						|
        if (!inserted) { | 
						|
            ret.push(first); | 
						|
        } | 
						|
    } | 
						|
    return ret; | 
						|
} | 
						|
 | 
						|
function createRoot() { | 
						|
    return new ReportNode(new Path([])); | 
						|
} | 
						|
 | 
						|
function createNestedSummary(coverageMap) { | 
						|
    const flattened = toInitialList(coverageMap); | 
						|
    const dirParents = toDirParents(flattened.list); | 
						|
    const topNodes = foldIntoParents(dirParents); | 
						|
 | 
						|
    if (topNodes.length === 0) { | 
						|
        return treeFor(new ReportNode(new Path([]))); | 
						|
    } | 
						|
 | 
						|
    if (topNodes.length === 1) { | 
						|
        return treeFor(topNodes[0]); | 
						|
    } | 
						|
 | 
						|
    const root = createRoot(); | 
						|
    topNodes.forEach(node => { | 
						|
        root.addChild(node); | 
						|
    }); | 
						|
    return treeFor(root); | 
						|
} | 
						|
 | 
						|
function createPackageSummary(coverageMap) { | 
						|
    const flattened = toInitialList(coverageMap); | 
						|
    const dirParents = toDirParents(flattened.list); | 
						|
    const common = flattened.commonParent; | 
						|
    let prefix; | 
						|
    let root; | 
						|
 | 
						|
    if (dirParents.length === 1) { | 
						|
        root = dirParents[0]; | 
						|
    } else { | 
						|
        root = createRoot(); | 
						|
        // if one of the dirs is itself the root, | 
						|
        // then we need to create a top-level dir | 
						|
        dirParents.forEach(dp => { | 
						|
            if (dp.path.length === 0) { | 
						|
                prefix = 'root'; | 
						|
            } | 
						|
        }); | 
						|
        if (prefix && common.length > 0) { | 
						|
            prefix = common.elements()[common.elements().length - 1]; | 
						|
        } | 
						|
        dirParents.forEach(node => { | 
						|
            root.addChild(node); | 
						|
        }); | 
						|
    } | 
						|
    return treeFor(root, prefix); | 
						|
} | 
						|
 | 
						|
function createFlatSummary(coverageMap) { | 
						|
    const flattened = toInitialList(coverageMap); | 
						|
    const list = flattened.list; | 
						|
    const root = createRoot(); | 
						|
 | 
						|
    list.forEach(o => { | 
						|
        const node = new ReportNode(o.path, o.fileCoverage); | 
						|
        root.addChild(node); | 
						|
    }); | 
						|
    return treeFor(root); | 
						|
} | 
						|
 | 
						|
module.exports = { | 
						|
    createNestedSummary, | 
						|
    createPackageSummary, | 
						|
    createFlatSummary | 
						|
};
 | 
						|
 |