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.
137 lines
3.7 KiB
137 lines
3.7 KiB
/* |
|
Copyright 2012-2015, Yahoo Inc. |
|
Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. |
|
*/ |
|
'use strict'; |
|
|
|
/** |
|
* 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 |
|
*/ |
|
class Visitor { |
|
constructor(delegate) { |
|
this.delegate = delegate; |
|
} |
|
} |
|
|
|
['Start', 'End', 'Summary', 'SummaryEnd', 'Detail'] |
|
.map(k => `on${k}`) |
|
.forEach(fn => { |
|
Object.defineProperty(Visitor.prototype, fn, { |
|
writable: true, |
|
value(node, state) { |
|
if (typeof this.delegate[fn] === 'function') { |
|
this.delegate[fn](node, state); |
|
} |
|
} |
|
}); |
|
}); |
|
|
|
class CompositeVisitor extends Visitor { |
|
constructor(visitors) { |
|
super(); |
|
|
|
if (!Array.isArray(visitors)) { |
|
visitors = [visitors]; |
|
} |
|
this.visitors = visitors.map(v => { |
|
if (v instanceof Visitor) { |
|
return v; |
|
} |
|
return new Visitor(v); |
|
}); |
|
} |
|
} |
|
|
|
['Start', 'Summary', 'SummaryEnd', 'Detail', 'End'] |
|
.map(k => `on${k}`) |
|
.forEach(fn => { |
|
Object.defineProperty(CompositeVisitor.prototype, fn, { |
|
value(node, state) { |
|
this.visitors.forEach(v => { |
|
v[fn](node, state); |
|
}); |
|
} |
|
}); |
|
}); |
|
|
|
class BaseNode { |
|
isRoot() { |
|
return !this.getParent(); |
|
} |
|
|
|
/** |
|
* 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 |
|
*/ |
|
visit(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 |
|
*/ |
|
class BaseTree { |
|
constructor(root) { |
|
this.root = root; |
|
} |
|
|
|
/** |
|
* returns the root node of the tree |
|
*/ |
|
getRoot() { |
|
return this.root; |
|
} |
|
|
|
/** |
|
* 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 |
|
*/ |
|
visit(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 = { |
|
BaseTree, |
|
BaseNode, |
|
Visitor, |
|
CompositeVisitor |
|
};
|
|
|