160 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			160 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
|  | /* | ||
|  |  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'); | ||
|  | /** | ||
|  |  * An object with methods that are called during the traversal of the coverage tree. | ||
|  |  * A visitor has the following methods that are called during tree traversal. | ||
|  |  * | ||
|  |  *   * `onStart(root, state)` - called before traversal begins | ||
|  |  *   * `onSummary(node, state)` - called for every summary node | ||
|  |  *   * `onDetail(node, state)` - called for every detail node | ||
|  |  *   * `onSummaryEnd(node, state)` - called after all children have been visited for | ||
|  |  *      a summary node. | ||
|  |  *   * `onEnd(root, state)` - called after traversal ends | ||
|  |  * | ||
|  |  * @param delegate - a partial visitor that only implements the methods of interest | ||
|  |  *  The visitor object supplies the missing methods as noops. For example, reports | ||
|  |  *  that only need the final coverage summary need implement `onStart` and nothing | ||
|  |  *  else. Reports that use only detailed coverage information need implement `onDetail` | ||
|  |  *  and nothing else. | ||
|  |  * @constructor | ||
|  |  */ | ||
|  | function Visitor(delegate) { | ||
|  |     this.delegate = delegate; | ||
|  | } | ||
|  | 
 | ||
|  | ['Start', 'End', 'Summary', 'SummaryEnd', 'Detail'].forEach(k => { | ||
|  |     const f = 'on' + k; | ||
|  |     Visitor.prototype[f] = function(node, state) { | ||
|  |         if (this.delegate[f] && typeof this.delegate[f] === 'function') { | ||
|  |             this.delegate[f].call(this.delegate, node, state); | ||
|  |         } | ||
|  |     }; | ||
|  | }); | ||
|  | 
 | ||
|  | function CompositeVisitor(visitors) { | ||
|  |     if (!Array.isArray(visitors)) { | ||
|  |         visitors = [visitors]; | ||
|  |     } | ||
|  |     this.visitors = visitors.map(v => { | ||
|  |         if (v instanceof Visitor) { | ||
|  |             return v; | ||
|  |         } | ||
|  |         return new Visitor(v); | ||
|  |     }); | ||
|  | } | ||
|  | 
 | ||
|  | util.inherits(CompositeVisitor, Visitor); | ||
|  | 
 | ||
|  | ['Start', 'Summary', 'SummaryEnd', 'Detail', 'End'].forEach(k => { | ||
|  |     const f = 'on' + k; | ||
|  |     CompositeVisitor.prototype[f] = function(node, state) { | ||
|  |         this.visitors.forEach(v => { | ||
|  |             v[f](node, state); | ||
|  |         }); | ||
|  |     }; | ||
|  | }); | ||
|  | 
 | ||
|  | function Node() {} | ||
|  | 
 | ||
|  | /* istanbul ignore next: abstract method */ | ||
|  | Node.prototype.getQualifiedName = function() { | ||
|  |     throw new Error('getQualifiedName must be overridden'); | ||
|  | }; | ||
|  | 
 | ||
|  | /* istanbul ignore next: abstract method */ | ||
|  | Node.prototype.getRelativeName = function() { | ||
|  |     throw new Error('getRelativeName must be overridden'); | ||
|  | }; | ||
|  | 
 | ||
|  | /* istanbul ignore next: abstract method */ | ||
|  | Node.prototype.isRoot = function() { | ||
|  |     return !this.getParent(); | ||
|  | }; | ||
|  | 
 | ||
|  | /* istanbul ignore next: abstract method */ | ||
|  | Node.prototype.getParent = function() { | ||
|  |     throw new Error('getParent must be overridden'); | ||
|  | }; | ||
|  | 
 | ||
|  | /* istanbul ignore next: abstract method */ | ||
|  | Node.prototype.getChildren = function() { | ||
|  |     throw new Error('getChildren must be overridden'); | ||
|  | }; | ||
|  | 
 | ||
|  | /* istanbul ignore next: abstract method */ | ||
|  | Node.prototype.isSummary = function() { | ||
|  |     throw new Error('isSummary must be overridden'); | ||
|  | }; | ||
|  | 
 | ||
|  | /* istanbul ignore next: abstract method */ | ||
|  | Node.prototype.getCoverageSummary = function(/* filesOnly */) { | ||
|  |     throw new Error('getCoverageSummary must be overridden'); | ||
|  | }; | ||
|  | 
 | ||
|  | /* istanbul ignore next: abstract method */ | ||
|  | Node.prototype.getFileCoverage = function() { | ||
|  |     throw new Error('getFileCoverage must be overridden'); | ||
|  | }; | ||
|  | /** | ||
|  |  * visit all nodes depth-first from this node down. Note that `onStart` | ||
|  |  * and `onEnd` are never called on the visitor even if the current | ||
|  |  * node is the root of the tree. | ||
|  |  * @param visitor a full visitor that is called during tree traversal | ||
|  |  * @param state optional state that is passed around | ||
|  |  */ | ||
|  | Node.prototype.visit = function(visitor, state) { | ||
|  |     if (this.isSummary()) { | ||
|  |         visitor.onSummary(this, state); | ||
|  |     } else { | ||
|  |         visitor.onDetail(this, state); | ||
|  |     } | ||
|  | 
 | ||
|  |     this.getChildren().forEach(child => { | ||
|  |         child.visit(visitor, state); | ||
|  |     }); | ||
|  | 
 | ||
|  |     if (this.isSummary()) { | ||
|  |         visitor.onSummaryEnd(this, state); | ||
|  |     } | ||
|  | }; | ||
|  | 
 | ||
|  | /** | ||
|  |  * abstract base class for a coverage tree. | ||
|  |  * @constructor | ||
|  |  */ | ||
|  | function Tree() {} | ||
|  | 
 | ||
|  | /** | ||
|  |  * returns the root node of the tree | ||
|  |  */ | ||
|  | /* istanbul ignore next: abstract method */ | ||
|  | Tree.prototype.getRoot = function() { | ||
|  |     throw new Error('getRoot must be overridden'); | ||
|  | }; | ||
|  | 
 | ||
|  | /** | ||
|  |  * visits the tree depth-first with the supplied partial visitor | ||
|  |  * @param visitor - a potentially partial visitor | ||
|  |  * @param state - the state to be passed around during tree traversal | ||
|  |  */ | ||
|  | Tree.prototype.visit = function(visitor, state) { | ||
|  |     if (!(visitor instanceof Visitor)) { | ||
|  |         visitor = new Visitor(visitor); | ||
|  |     } | ||
|  |     visitor.onStart(this.getRoot(), state); | ||
|  |     this.getRoot().visit(visitor, state); | ||
|  |     visitor.onEnd(this.getRoot(), state); | ||
|  | }; | ||
|  | 
 | ||
|  | module.exports = { | ||
|  |     Tree, | ||
|  |     Node, | ||
|  |     Visitor, | ||
|  |     CompositeVisitor | ||
|  | }; |