UNPKG

3.81 kBJavaScriptView Raw
1/*
2 Copyright 2012-2015, Yahoo Inc.
3 Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
4 */
5'use strict';
6
7/**
8 * An object with methods that are called during the traversal of the coverage tree.
9 * A visitor has the following methods that are called during tree traversal.
10 *
11 * * `onStart(root, state)` - called before traversal begins
12 * * `onSummary(node, state)` - called for every summary node
13 * * `onDetail(node, state)` - called for every detail node
14 * * `onSummaryEnd(node, state)` - called after all children have been visited for
15 * a summary node.
16 * * `onEnd(root, state)` - called after traversal ends
17 *
18 * @param delegate - a partial visitor that only implements the methods of interest
19 * The visitor object supplies the missing methods as noops. For example, reports
20 * that only need the final coverage summary need implement `onStart` and nothing
21 * else. Reports that use only detailed coverage information need implement `onDetail`
22 * and nothing else.
23 * @constructor
24 */
25class Visitor {
26 constructor(delegate) {
27 this.delegate = delegate;
28 }
29}
30
31['Start', 'End', 'Summary', 'SummaryEnd', 'Detail']
32 .map(k => `on${k}`)
33 .forEach(fn => {
34 Object.defineProperty(Visitor.prototype, fn, {
35 writable: true,
36 value(node, state) {
37 if (typeof this.delegate[fn] === 'function') {
38 this.delegate[fn](node, state);
39 }
40 }
41 });
42 });
43
44class CompositeVisitor extends Visitor {
45 constructor(visitors) {
46 super();
47
48 if (!Array.isArray(visitors)) {
49 visitors = [visitors];
50 }
51 this.visitors = visitors.map(v => {
52 if (v instanceof Visitor) {
53 return v;
54 }
55 return new Visitor(v);
56 });
57 }
58}
59
60['Start', 'Summary', 'SummaryEnd', 'Detail', 'End']
61 .map(k => `on${k}`)
62 .forEach(fn => {
63 Object.defineProperty(CompositeVisitor.prototype, fn, {
64 value(node, state) {
65 this.visitors.forEach(v => {
66 v[fn](node, state);
67 });
68 }
69 });
70 });
71
72class BaseNode {
73 isRoot() {
74 return !this.getParent();
75 }
76
77 /**
78 * visit all nodes depth-first from this node down. Note that `onStart`
79 * and `onEnd` are never called on the visitor even if the current
80 * node is the root of the tree.
81 * @param visitor a full visitor that is called during tree traversal
82 * @param state optional state that is passed around
83 */
84 visit(visitor, state) {
85 if (this.isSummary()) {
86 visitor.onSummary(this, state);
87 } else {
88 visitor.onDetail(this, state);
89 }
90
91 this.getChildren().forEach(child => {
92 child.visit(visitor, state);
93 });
94
95 if (this.isSummary()) {
96 visitor.onSummaryEnd(this, state);
97 }
98 }
99}
100
101/**
102 * abstract base class for a coverage tree.
103 * @constructor
104 */
105class BaseTree {
106 constructor(root) {
107 this.root = root;
108 }
109
110 /**
111 * returns the root node of the tree
112 */
113 getRoot() {
114 return this.root;
115 }
116
117 /**
118 * visits the tree depth-first with the supplied partial visitor
119 * @param visitor - a potentially partial visitor
120 * @param state - the state to be passed around during tree traversal
121 */
122 visit(visitor, state) {
123 if (!(visitor instanceof Visitor)) {
124 visitor = new Visitor(visitor);
125 }
126 visitor.onStart(this.getRoot(), state);
127 this.getRoot().visit(visitor, state);
128 visitor.onEnd(this.getRoot(), state);
129 }
130}
131
132module.exports = {
133 BaseTree,
134 BaseNode,
135 Visitor,
136 CompositeVisitor
137};