| /* |
| 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 |
| }; |