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