phantomas 2.0.0 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Dockerfile +39 -25
- package/README.md +20 -8
- package/bin/phantomas.js +9 -164
- package/bin/program.js +198 -0
- package/bin/utils.js +16 -0
- package/core/modules/navigationTiming/scope.js +7 -1
- package/core/modules/requestsMonitor/requestsMonitor.js +30 -13
- package/core/scope.js +2 -1
- package/extensions/cookies/cookies.js +1 -2
- package/extensions/filmStrip/filmStrip.js +1 -1
- package/extensions/pageSource/pageSource.js +2 -0
- package/extensions/pageStorage/pageStorage.js +82 -0
- package/extensions/screenshot/screenshot.js +27 -9
- package/extensions/scroll/scroll.js +3 -1
- package/extensions/userAgent/userAgent.js +55 -0
- package/extensions/viewport/viewport.js +1 -1
- package/extensions/waitForSelector/waitForSelector.js +0 -1
- package/hooks/build +3 -0
- package/lib/browser.js +81 -33
- package/lib/index.js +18 -9
- package/lib/loader.js +3 -4
- package/lib/metadata/metadata.json +180 -29
- package/modules/ajaxRequests/scope.js +1 -1
- package/modules/analyzeCss/analyzeCss.js +79 -76
- package/modules/analyzeCss/scope.js +1 -1
- package/modules/blockDomains/blockDomains.js +21 -21
- package/modules/cacheHits/cacheHits.js +6 -3
- package/modules/cpuTasks/cpuTasks.js +22 -0
- package/modules/documentHeight/scope.js +1 -1
- package/modules/domComplexity/scope.js +1 -1
- package/modules/domHiddenContent/scope.js +1 -1
- package/modules/domMutations/scope.js +1 -1
- package/modules/domQueries/domQueries.js +16 -19
- package/modules/domQueries/scope.js +1 -1
- package/modules/domains/domains.js +1 -1
- package/modules/events/scope.js +2 -1
- package/modules/globalVariables/scope.js +1 -1
- package/modules/jQuery/scope.js +1 -1
- package/modules/javaScriptBottlenecks/scope.js +1 -1
- package/modules/lazyLoadableImages/scope.js +2 -2
- package/modules/localStorage/scope.js +1 -1
- package/modules/protocols/protocols.js +101 -0
- package/modules/requestsStats/requestsStats.js +1 -1
- package/modules/staticAssets/staticAssets.js +2 -1
- package/modules/windowPerformance/windowPerformance.js +1 -0
- package/package.json +31 -20
- package/lib/fast-stats.js +0 -634
- package/lib/metadata/generate.js +0 -283
- package/lib/metadata/make_docs.js +0 -185
- package/reporters/csv.js +0 -54
- package/reporters/plain.js +0 -173
- package/reporters/statsd.js +0 -82
- package/reporters/tap.js +0 -71
- package/reporters/teamcity.js +0 -73
package/lib/metadata/generate.js
DELETED
|
@@ -1,283 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Generates metadata.json file that stores metadata about:
|
|
5
|
-
*
|
|
6
|
-
* - events
|
|
7
|
-
* - metrics
|
|
8
|
-
* - modules
|
|
9
|
-
* - extensions
|
|
10
|
-
*/
|
|
11
|
-
"use strict";
|
|
12
|
-
|
|
13
|
-
var debug = require("debug")("generate"),
|
|
14
|
-
fs = require("fs"),
|
|
15
|
-
glob = require("glob"),
|
|
16
|
-
phantomas = require("../../"),
|
|
17
|
-
yaml = require("js-yaml"),
|
|
18
|
-
metadata = {
|
|
19
|
-
events: {},
|
|
20
|
-
extensions: {},
|
|
21
|
-
modules: {},
|
|
22
|
-
metrics: {},
|
|
23
|
-
metricsCount: 0,
|
|
24
|
-
modulesCount: 0,
|
|
25
|
-
extensionsCount: 0,
|
|
26
|
-
version: phantomas.version,
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
function getMetricsCoveredByTests(spec) {
|
|
30
|
-
const debug = require("debug")("metricsCovered");
|
|
31
|
-
|
|
32
|
-
var covered = {};
|
|
33
|
-
|
|
34
|
-
spec.forEach((testCase) => {
|
|
35
|
-
debug(testCase.label || testCase.url);
|
|
36
|
-
|
|
37
|
-
Object.keys(testCase.metrics || {}).forEach((metric) => {
|
|
38
|
-
covered[metric] = true;
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
Object.keys(testCase.offenders || {}).forEach((metric) => {
|
|
42
|
-
covered[metric] = true;
|
|
43
|
-
});
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
return Object.keys(covered);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function getOptionsCoveredByTests(spec) {
|
|
50
|
-
var covered = {};
|
|
51
|
-
|
|
52
|
-
spec.forEach((testCase) => {
|
|
53
|
-
Object.keys(testCase.options || {}).forEach((option) => {
|
|
54
|
-
covered[option] = true;
|
|
55
|
-
});
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
return Object.keys(covered).sort();
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function getModuleMetadata(moduleFile) {
|
|
62
|
-
var content = fs.readFileSync(moduleFile).toString(),
|
|
63
|
-
data = {
|
|
64
|
-
name: "",
|
|
65
|
-
description: "",
|
|
66
|
-
metrics: {},
|
|
67
|
-
events: {},
|
|
68
|
-
},
|
|
69
|
-
matches,
|
|
70
|
-
moduleName,
|
|
71
|
-
metricRegEx = /(setMetric|setMetricEvaluate|incrMetric)\(['"]([^'"]+)['"](\)|,)(.*@desc.*$)?/gm,
|
|
72
|
-
eventsRegEx = /emit\(['"]([^'"]+)['"]([^)]+).*@desc(.*)$/gm;
|
|
73
|
-
|
|
74
|
-
data.name = moduleName = moduleFile.split("/").pop().replace(/\.js$/, "");
|
|
75
|
-
|
|
76
|
-
const localPath = moduleFile.replace(
|
|
77
|
-
fs.realpathSync(__dirname + "/../../"),
|
|
78
|
-
""
|
|
79
|
-
);
|
|
80
|
-
data.localPath = localPath;
|
|
81
|
-
|
|
82
|
-
//
|
|
83
|
-
// scan the source code
|
|
84
|
-
//
|
|
85
|
-
|
|
86
|
-
// look for metrics metadata
|
|
87
|
-
while ((matches = metricRegEx.exec(content)) !== null) {
|
|
88
|
-
var entry = {},
|
|
89
|
-
metricName = matches[2],
|
|
90
|
-
metricComment = matches[4],
|
|
91
|
-
metricUnit = "ms",
|
|
92
|
-
hasDesc = metricComment && metricComment.indexOf("@desc") > -1;
|
|
93
|
-
|
|
94
|
-
if (typeof data.metrics[metricName] !== "undefined") {
|
|
95
|
-
if (hasDesc) {
|
|
96
|
-
debug(
|
|
97
|
-
"Found duplicated definition of %s metric in %s module",
|
|
98
|
-
metricName,
|
|
99
|
-
moduleName
|
|
100
|
-
);
|
|
101
|
-
}
|
|
102
|
-
continue;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// parse @desc
|
|
106
|
-
if (hasDesc) {
|
|
107
|
-
metricComment = metricComment.split("@desc").pop().trim();
|
|
108
|
-
entry.desc = "";
|
|
109
|
-
|
|
110
|
-
["unreliable", "optional", "offenders", "gecko"].forEach(function (
|
|
111
|
-
marker
|
|
112
|
-
) {
|
|
113
|
-
if (metricComment.indexOf("@" + marker) > -1) {
|
|
114
|
-
entry[marker] = true;
|
|
115
|
-
metricComment = metricComment.replace("@" + marker, "").trim();
|
|
116
|
-
}
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
// detect units (defaults to ms)
|
|
120
|
-
if ((matches = metricComment.match(/\[([^\]]+)\]/)) !== null) {
|
|
121
|
-
metricUnit = matches[1];
|
|
122
|
-
metricComment = metricComment.replace(matches[0], "").trim();
|
|
123
|
-
} else if (
|
|
124
|
-
/^(number of|total number of|average specificity|total specificity|median of number|maximum number|maximum level)/.test(
|
|
125
|
-
metricComment
|
|
126
|
-
)
|
|
127
|
-
) {
|
|
128
|
-
metricUnit = "number";
|
|
129
|
-
} else if (
|
|
130
|
-
/^(the size|size|length|total length) of/.test(metricComment)
|
|
131
|
-
) {
|
|
132
|
-
metricUnit = "bytes";
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// check if offenders are reported for this metric
|
|
136
|
-
if (content.indexOf("phantomas.addOffender('" + metricName + "'") > -1) {
|
|
137
|
-
entry.offenders = true;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
entry.desc = metricComment;
|
|
141
|
-
entry.unit = metricUnit;
|
|
142
|
-
} else if (moduleName !== "scope") {
|
|
143
|
-
debug(
|
|
144
|
-
"Metadata missing for %s metric in %s module",
|
|
145
|
-
metricName,
|
|
146
|
-
moduleName
|
|
147
|
-
);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
entry.module = moduleName;
|
|
151
|
-
|
|
152
|
-
// add a metric
|
|
153
|
-
data.metrics[metricName] = entry;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// look for events metadata
|
|
157
|
-
while ((matches = eventsRegEx.exec(content)) !== null) {
|
|
158
|
-
// console.log([moduleName, matches[1], matches[2], matches[3]]);
|
|
159
|
-
|
|
160
|
-
data.events[matches[1]] = {
|
|
161
|
-
file: localPath,
|
|
162
|
-
desc: matches[3].trim(),
|
|
163
|
-
arguments: matches[2].replace(/^[,\s]+/g, ""),
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// parse modules and extensions initial commit block
|
|
168
|
-
matches = /^\/\*\*([^/]+)\*\//m.exec(content);
|
|
169
|
-
if (matches) {
|
|
170
|
-
var desc = matches[0]
|
|
171
|
-
.replace(/^[\s/]?\*+\s?/gm, "") // remove the beginning of block comment
|
|
172
|
-
.replace(/\/$/gm, "") // remove the ending of block comment
|
|
173
|
-
.replace(/^\s?setMetric.*$/gm, "") // remove setMetric annotations
|
|
174
|
-
.trim();
|
|
175
|
-
|
|
176
|
-
//console.log(localPath, moduleName, matches[0]);
|
|
177
|
-
|
|
178
|
-
data.description = desc;
|
|
179
|
-
}
|
|
180
|
-
//console.log(data); throw new Error('foo');
|
|
181
|
-
|
|
182
|
-
return data;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// read the YAML spec file
|
|
186
|
-
const raw = fs
|
|
187
|
-
.readFileSync(__dirname + "/../../test/integration-spec.yaml")
|
|
188
|
-
.toString(),
|
|
189
|
-
spec = yaml.safeLoad(raw);
|
|
190
|
-
|
|
191
|
-
// find all metrics covered by integration tests (take a look into integration-spec.yaml)
|
|
192
|
-
const coveredMetrics = getMetricsCoveredByTests(spec);
|
|
193
|
-
|
|
194
|
-
debug(
|
|
195
|
-
"Found %d metrics covered by integration-spec.yaml...",
|
|
196
|
-
coveredMetrics.length
|
|
197
|
-
);
|
|
198
|
-
|
|
199
|
-
// find all options covered by integration tests (take a look into integration-spec.yaml)
|
|
200
|
-
const coveredOptions = getOptionsCoveredByTests(spec);
|
|
201
|
-
|
|
202
|
-
debug(
|
|
203
|
-
"Found %d options covered by integration-spec.yaml: --%s",
|
|
204
|
-
coveredOptions.length,
|
|
205
|
-
coveredOptions.join(", --")
|
|
206
|
-
);
|
|
207
|
-
|
|
208
|
-
// find all modules
|
|
209
|
-
var dir = phantomas.path;
|
|
210
|
-
debug("Looking for modules in %s...", dir);
|
|
211
|
-
|
|
212
|
-
[]
|
|
213
|
-
.concat(
|
|
214
|
-
glob.sync(dir + "/core/modules/**/*.js"),
|
|
215
|
-
glob.sync(dir + "/modules/**/*.js"),
|
|
216
|
-
glob.sync(dir + "/extensions/**/*.js"),
|
|
217
|
-
glob.sync(dir + "/lib/*.js")
|
|
218
|
-
)
|
|
219
|
-
.forEach(function (moduleFile) {
|
|
220
|
-
const data = getModuleMetadata(moduleFile);
|
|
221
|
-
|
|
222
|
-
// skip scope.js files when listing metrics
|
|
223
|
-
if (
|
|
224
|
-
moduleFile.indexOf("/scope.js") === -1 &&
|
|
225
|
-
moduleFile.indexOf("/lib/") === -1
|
|
226
|
-
) {
|
|
227
|
-
Object.keys(data.metrics).forEach(function (metricName) {
|
|
228
|
-
metadata.metricsCount++;
|
|
229
|
-
metadata.metrics[metricName] = data.metrics[metricName];
|
|
230
|
-
metadata.metrics[metricName].testsCovered =
|
|
231
|
-
coveredMetrics.indexOf(metricName) > -1;
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
// modules ...
|
|
236
|
-
if (
|
|
237
|
-
moduleFile.indexOf("/scope.js") === -1 &&
|
|
238
|
-
moduleFile.indexOf("/modules/") > -1
|
|
239
|
-
) {
|
|
240
|
-
metadata.modules[data.name] = {
|
|
241
|
-
file: data.localPath,
|
|
242
|
-
desc: data.description,
|
|
243
|
-
events: Object.keys(data.events),
|
|
244
|
-
metrics: Object.keys(data.metrics),
|
|
245
|
-
};
|
|
246
|
-
|
|
247
|
-
metadata.modulesCount++;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// ... and extensions
|
|
251
|
-
if (moduleFile.indexOf("/extensions/") > -1) {
|
|
252
|
-
metadata.extensions[data.name] = {
|
|
253
|
-
file: data.localPath,
|
|
254
|
-
desc: data.description,
|
|
255
|
-
options: [],
|
|
256
|
-
};
|
|
257
|
-
|
|
258
|
-
metadata.extensionsCount++;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
Object.keys(data.events).forEach(function (eventName) {
|
|
262
|
-
metadata.events[eventName] = data.events[eventName];
|
|
263
|
-
});
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
// store metadata
|
|
267
|
-
var filename = __dirname + "/metadata.json",
|
|
268
|
-
content = JSON.stringify(metadata, null, " ");
|
|
269
|
-
|
|
270
|
-
debug("Storing metadata in %s...", filename);
|
|
271
|
-
fs.writeFile(filename, content, function (err) {
|
|
272
|
-
if (err) {
|
|
273
|
-
debug("Storing failed: %s", err);
|
|
274
|
-
} else {
|
|
275
|
-
debug("Done");
|
|
276
|
-
}
|
|
277
|
-
});
|
|
278
|
-
|
|
279
|
-
debug(
|
|
280
|
-
"Metrics found: %d (tests coverage: %s%)",
|
|
281
|
-
metadata.metricsCount,
|
|
282
|
-
((coveredMetrics.length / metadata.metricsCount) * 100).toFixed(2)
|
|
283
|
-
);
|
|
@@ -1,185 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Generates Markdown files in /docs directory
|
|
3
|
-
*/
|
|
4
|
-
const debug = require("debug")("docs"),
|
|
5
|
-
phantomas = require("../.."),
|
|
6
|
-
fs = require("fs"),
|
|
7
|
-
metadata_file = __dirname + "/metadata.json",
|
|
8
|
-
docs_dir = fs.realpathSync(__dirname + "/../../docs"),
|
|
9
|
-
github_root = "https://github.com/macbre/phantomas/tree/devel";
|
|
10
|
-
|
|
11
|
-
debug("Generating docs into %s ...", docs_dir);
|
|
12
|
-
debug("Reading %s ...", metadata_file);
|
|
13
|
-
|
|
14
|
-
const metadata = JSON.parse(fs.readFileSync(metadata_file));
|
|
15
|
-
|
|
16
|
-
// console.log(metadata);
|
|
17
|
-
|
|
18
|
-
const docs_notice =
|
|
19
|
-
"\n\n---\n> This file is auto-generated from code comments. Please run `npm run make-docs` to update it.";
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* events.md
|
|
23
|
-
*
|
|
24
|
-
* https://github.com/macbre/phantomas/issues/729
|
|
25
|
-
*/
|
|
26
|
-
var events = `
|
|
27
|
-
Events
|
|
28
|
-
======
|
|
29
|
-
|
|
30
|
-
`;
|
|
31
|
-
|
|
32
|
-
Object.keys(metadata.events)
|
|
33
|
-
.sort()
|
|
34
|
-
.forEach((eventName) => {
|
|
35
|
-
const entry = metadata.events[eventName];
|
|
36
|
-
|
|
37
|
-
events += `
|
|
38
|
-
### ${eventName}
|
|
39
|
-
|
|
40
|
-
**Description**: ${entry.desc}
|
|
41
|
-
|
|
42
|
-
**Arguments**: ${entry.arguments}
|
|
43
|
-
|
|
44
|
-
[View source](${github_root}${entry.file})
|
|
45
|
-
|
|
46
|
-
`.trim();
|
|
47
|
-
|
|
48
|
-
events += "\n\n\n";
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
async function buildDocs() {
|
|
52
|
-
// now add some real life examples from loading an example URL from our tests
|
|
53
|
-
events += `## Examples`;
|
|
54
|
-
|
|
55
|
-
const promise = phantomas("http://0.0.0.0:8888/_make_docs.html");
|
|
56
|
-
var hasEvent = {};
|
|
57
|
-
|
|
58
|
-
[
|
|
59
|
-
"recv",
|
|
60
|
-
"send",
|
|
61
|
-
"request",
|
|
62
|
-
"response",
|
|
63
|
-
"milestone",
|
|
64
|
-
"metrics",
|
|
65
|
-
"consoleLog",
|
|
66
|
-
"jserror",
|
|
67
|
-
].forEach((eventName) => {
|
|
68
|
-
promise.on(eventName, (...args) => {
|
|
69
|
-
if (hasEvent[eventName]) return;
|
|
70
|
-
hasEvent[eventName] = true;
|
|
71
|
-
|
|
72
|
-
const dumped = JSON.stringify(args, null, " ");
|
|
73
|
-
debug("events.md: %s event triggered ...", eventName);
|
|
74
|
-
|
|
75
|
-
events += `
|
|
76
|
-
|
|
77
|
-
### ${eventName}
|
|
78
|
-
|
|
79
|
-
Arguments passed to the event:
|
|
80
|
-
|
|
81
|
-
\`\`\`json
|
|
82
|
-
${dumped}
|
|
83
|
-
\`\`\`
|
|
84
|
-
`.trimRight();
|
|
85
|
-
});
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
// make results available when generating metrics.md - see the next step
|
|
89
|
-
const runResults = await promise;
|
|
90
|
-
|
|
91
|
-
debug("Report metrics: %j", runResults.getMetrics());
|
|
92
|
-
debug("Report offenders: %j", runResults.getAllOffenders());
|
|
93
|
-
|
|
94
|
-
debug("Saving events.md ...");
|
|
95
|
-
fs.writeFileSync(docs_dir + "/events.md", events.trim() + docs_notice);
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* metrics.md
|
|
99
|
-
*
|
|
100
|
-
* https://github.com/macbre/phantomas/issues/729
|
|
101
|
-
*/
|
|
102
|
-
|
|
103
|
-
// get offender examples from integration-spec.yaml
|
|
104
|
-
const offenders = (() => {
|
|
105
|
-
const yaml = require("js-yaml"),
|
|
106
|
-
spec = yaml.safeLoad(
|
|
107
|
-
fs
|
|
108
|
-
.readFileSync(__dirname + "/../../test/integration-spec.yaml")
|
|
109
|
-
.toString()
|
|
110
|
-
);
|
|
111
|
-
|
|
112
|
-
var offenders = {};
|
|
113
|
-
|
|
114
|
-
spec.forEach((testCase) => {
|
|
115
|
-
Object.keys(testCase.offenders || {}).forEach((metricName) => {
|
|
116
|
-
if (!offenders[metricName]) {
|
|
117
|
-
offenders[metricName] = testCase.offenders[metricName][0];
|
|
118
|
-
}
|
|
119
|
-
});
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
// test coverage is not 100%, fill missing offenders using the "real" phantomas run from above
|
|
123
|
-
Object.keys(runResults.getMetrics()).forEach((metricName) => {
|
|
124
|
-
if (!offenders[metricName] && runResults.getOffenders(metricName)) {
|
|
125
|
-
offenders[metricName] = runResults.getOffenders(metricName)[0];
|
|
126
|
-
}
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
return offenders;
|
|
130
|
-
})();
|
|
131
|
-
|
|
132
|
-
// build Markdown file
|
|
133
|
-
var metrics = `
|
|
134
|
-
Modules and metrics
|
|
135
|
-
===================
|
|
136
|
-
|
|
137
|
-
This file describes all [\`phantomas\` modules](https://github.com/macbre/phantomas/tree/devel/modules) (${metadata.modulesCount} of them) and ${metadata.metricsCount} metrics that they emit.
|
|
138
|
-
|
|
139
|
-
When applicable, [offender](https://github.com/macbre/phantomas/issues/140) example is provided.
|
|
140
|
-
|
|
141
|
-
`;
|
|
142
|
-
|
|
143
|
-
Object.keys(metadata.modules)
|
|
144
|
-
.sort()
|
|
145
|
-
.forEach((moduleName) => {
|
|
146
|
-
const entry = metadata.modules[moduleName];
|
|
147
|
-
|
|
148
|
-
metrics += `
|
|
149
|
-
|
|
150
|
-
## [${moduleName}](${github_root}${entry.file})
|
|
151
|
-
|
|
152
|
-
> ${entry.desc}
|
|
153
|
-
`;
|
|
154
|
-
|
|
155
|
-
entry.metrics.sort().forEach((metricName) => {
|
|
156
|
-
const metric = metadata.metrics[metricName],
|
|
157
|
-
hasOffenders = metric.offenders ? ", with offenders" : "";
|
|
158
|
-
|
|
159
|
-
metrics += `
|
|
160
|
-
##### \`${metricName}\`
|
|
161
|
-
|
|
162
|
-
${metric.desc} (${metric.unit}${hasOffenders})
|
|
163
|
-
`;
|
|
164
|
-
|
|
165
|
-
// add offender's example
|
|
166
|
-
if (hasOffenders && offenders[metricName]) {
|
|
167
|
-
const dumped = JSON.stringify(offenders[metricName], null, " ");
|
|
168
|
-
|
|
169
|
-
metrics += `
|
|
170
|
-
\`\`\`json
|
|
171
|
-
${dumped}
|
|
172
|
-
\`\`\`
|
|
173
|
-
`;
|
|
174
|
-
}
|
|
175
|
-
});
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
debug("Saving metrics.md ...");
|
|
179
|
-
fs.writeFileSync(docs_dir + "/metrics.md", metrics.trim() + docs_notice);
|
|
180
|
-
|
|
181
|
-
// ---
|
|
182
|
-
debug("Done");
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
buildDocs();
|
package/reporters/csv.js
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Results formatter for -R csv
|
|
3
|
-
*
|
|
4
|
-
* Options:
|
|
5
|
-
* no-header - omit CSV header
|
|
6
|
-
* timestamp - add the current timestamp as the first column
|
|
7
|
-
* url - add the URL as the first column
|
|
8
|
-
*
|
|
9
|
-
* @see https://github.com/touv/node-csv-string
|
|
10
|
-
*/
|
|
11
|
-
"use strict";
|
|
12
|
-
|
|
13
|
-
var CSV = require("csv-string");
|
|
14
|
-
|
|
15
|
-
module.exports = function (results, reporterOptions) {
|
|
16
|
-
// public API
|
|
17
|
-
return {
|
|
18
|
-
render: function () {
|
|
19
|
-
var metrics = results.getMetricsNames(),
|
|
20
|
-
keys = [],
|
|
21
|
-
values = [],
|
|
22
|
-
ret = [];
|
|
23
|
-
|
|
24
|
-
metrics.forEach(function (metric) {
|
|
25
|
-
var value = results.getMetric(metric);
|
|
26
|
-
|
|
27
|
-
keys.push(metric);
|
|
28
|
-
values.push(value);
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
// -R csv:url
|
|
32
|
-
if (reporterOptions.url === true) {
|
|
33
|
-
values.unshift(results.getUrl());
|
|
34
|
-
keys.unshift("url");
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// -R csv:timestamp
|
|
38
|
-
if (reporterOptions.timestamp === true) {
|
|
39
|
-
values.unshift(new Date().toJSON().substr(0, 19));
|
|
40
|
-
keys.unshift("timestamp");
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// -R csv:no-header
|
|
44
|
-
if (reporterOptions["no-header"] !== true) {
|
|
45
|
-
ret.push(keys);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// add the values
|
|
49
|
-
ret.push(values);
|
|
50
|
-
|
|
51
|
-
return CSV.stringify(ret);
|
|
52
|
-
},
|
|
53
|
-
};
|
|
54
|
-
};
|
package/reporters/plain.js
DELETED
|
@@ -1,173 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Results formatter for --format=plain
|
|
3
|
-
*
|
|
4
|
-
* Options:
|
|
5
|
-
* no-color - disable ANSI colors
|
|
6
|
-
*/
|
|
7
|
-
"use strict";
|
|
8
|
-
|
|
9
|
-
var colors = require("../lib/ansicolors"),
|
|
10
|
-
metrics = require("../lib/index").metadata.metrics,
|
|
11
|
-
fold = require("travis-fold"),
|
|
12
|
-
rpad = require("../core/pads").rpad,
|
|
13
|
-
OK = "✓",
|
|
14
|
-
ERR = "✗";
|
|
15
|
-
|
|
16
|
-
function getMetricUnit(metricName) {
|
|
17
|
-
var entry = metrics[metricName];
|
|
18
|
-
return typeof entry !== "undefined" &&
|
|
19
|
-
entry.unit !== "number" &&
|
|
20
|
-
entry.unit !== "string"
|
|
21
|
-
? entry.unit
|
|
22
|
-
: false;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
module.exports = function (results, reporterOptions) {
|
|
26
|
-
var isMultiple = Array.isArray(results),
|
|
27
|
-
noColor = reporterOptions["no-color"] === true;
|
|
28
|
-
|
|
29
|
-
function formatSingleRunResults(results) {
|
|
30
|
-
var res = [];
|
|
31
|
-
|
|
32
|
-
if (noColor) {
|
|
33
|
-
colors.disable();
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// header
|
|
37
|
-
res.push(
|
|
38
|
-
results.getGenerator() + " metrics for <" + results.getUrl() + ">:"
|
|
39
|
-
);
|
|
40
|
-
res.push("");
|
|
41
|
-
|
|
42
|
-
// metrics
|
|
43
|
-
fold.pushStart(res, "metrics");
|
|
44
|
-
|
|
45
|
-
results.getMetricsNames().forEach(function (metric) {
|
|
46
|
-
var line = " " + metric + ": " + results.getMetric(metric),
|
|
47
|
-
unit = getMetricUnit(metric);
|
|
48
|
-
|
|
49
|
-
if (unit !== false) {
|
|
50
|
-
line += " " + colors.brightBlack(unit);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// check asserts
|
|
54
|
-
if (results.hasAssertion(metric)) {
|
|
55
|
-
if (results.assert(metric)) {
|
|
56
|
-
line = colors.brightGreen(OK + line);
|
|
57
|
-
} else {
|
|
58
|
-
line =
|
|
59
|
-
rpad(ERR + line, 50) +
|
|
60
|
-
"Assertion failed! Expected to be less than or equal: " +
|
|
61
|
-
results.getAssertion(metric);
|
|
62
|
-
line = colors.brightRed(line);
|
|
63
|
-
}
|
|
64
|
-
} else {
|
|
65
|
-
line = "*" + line;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
res.push(line);
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
fold.pushEnd(res, "metrics");
|
|
72
|
-
res.push("");
|
|
73
|
-
|
|
74
|
-
// offenders
|
|
75
|
-
var offenders = results.getAllOffenders();
|
|
76
|
-
fold.pushStart(res, "offenders");
|
|
77
|
-
|
|
78
|
-
Object.keys(offenders).forEach(function (metric) {
|
|
79
|
-
var LIMIT = 50,
|
|
80
|
-
offenders = results.getOffenders(metric),
|
|
81
|
-
len = offenders.length;
|
|
82
|
-
|
|
83
|
-
res.push(
|
|
84
|
-
colors.brightGreen(
|
|
85
|
-
"Offenders for " + metric + " (" + results.getMetric(metric) + "):"
|
|
86
|
-
)
|
|
87
|
-
);
|
|
88
|
-
|
|
89
|
-
// limit the ammount of offenders emitted
|
|
90
|
-
offenders.slice(0, LIMIT).forEach(function (msg) {
|
|
91
|
-
res.push(" * " + msg);
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
if (len > LIMIT) {
|
|
95
|
-
res.push(colors.brightBlack("(" + (len - LIMIT) + " more)"));
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
res.push("");
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
fold.pushEnd(res, "offenders");
|
|
102
|
-
|
|
103
|
-
return fold.wrap(results.getUrl(), res.join("\n").trim()) + "\n";
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
function formatMultipleRunResults(results) {
|
|
107
|
-
var AsciiTable = require("ascii-table"),
|
|
108
|
-
format = require("util").format,
|
|
109
|
-
stats = new (require("../lib/stats"))(),
|
|
110
|
-
runs = results.length,
|
|
111
|
-
table;
|
|
112
|
-
|
|
113
|
-
// table title and heading
|
|
114
|
-
table = new AsciiTable();
|
|
115
|
-
table.setTitle(
|
|
116
|
-
format(
|
|
117
|
-
"Report from %d run(s) for <%s> using %s",
|
|
118
|
-
runs,
|
|
119
|
-
results[0].getUrl(),
|
|
120
|
-
results[0].getGenerator()
|
|
121
|
-
)
|
|
122
|
-
);
|
|
123
|
-
table.setTitleAlignLeft();
|
|
124
|
-
|
|
125
|
-
var heading = [];
|
|
126
|
-
heading.push(AsciiTable.alignCenter("Metric", 30));
|
|
127
|
-
|
|
128
|
-
stats.getAvailableStats().forEach(function (name) {
|
|
129
|
-
heading.push(AsciiTable.alignCenter(name, 12));
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
table.setHeading(heading);
|
|
133
|
-
|
|
134
|
-
// metrics stats
|
|
135
|
-
for (var i = 0; i < runs; i++) {
|
|
136
|
-
stats.pushMetrics(results[i].getMetrics());
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// generate rows (one for each metric)
|
|
140
|
-
stats.getMetrics().forEach(function (metricName) {
|
|
141
|
-
var row = [],
|
|
142
|
-
metricStats = stats.getMetricStats(metricName),
|
|
143
|
-
unit = getMetricUnit(metricName),
|
|
144
|
-
heading = metricName;
|
|
145
|
-
|
|
146
|
-
if (unit !== false) {
|
|
147
|
-
heading += " [" + unit + "]";
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
row.push(heading);
|
|
151
|
-
|
|
152
|
-
Object.keys(metricStats).forEach(function (stat) {
|
|
153
|
-
row.push(metricStats[stat]);
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
table.addRow(row);
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
return table.toString() + "\n";
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// public API
|
|
163
|
-
return {
|
|
164
|
-
handlesMultiple: true,
|
|
165
|
-
render: function () {
|
|
166
|
-
if (!isMultiple) {
|
|
167
|
-
return formatSingleRunResults(results);
|
|
168
|
-
} else {
|
|
169
|
-
return formatMultipleRunResults(results);
|
|
170
|
-
}
|
|
171
|
-
},
|
|
172
|
-
};
|
|
173
|
-
};
|