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.
Files changed (54) hide show
  1. package/Dockerfile +39 -25
  2. package/README.md +20 -8
  3. package/bin/phantomas.js +9 -164
  4. package/bin/program.js +198 -0
  5. package/bin/utils.js +16 -0
  6. package/core/modules/navigationTiming/scope.js +7 -1
  7. package/core/modules/requestsMonitor/requestsMonitor.js +30 -13
  8. package/core/scope.js +2 -1
  9. package/extensions/cookies/cookies.js +1 -2
  10. package/extensions/filmStrip/filmStrip.js +1 -1
  11. package/extensions/pageSource/pageSource.js +2 -0
  12. package/extensions/pageStorage/pageStorage.js +82 -0
  13. package/extensions/screenshot/screenshot.js +27 -9
  14. package/extensions/scroll/scroll.js +3 -1
  15. package/extensions/userAgent/userAgent.js +55 -0
  16. package/extensions/viewport/viewport.js +1 -1
  17. package/extensions/waitForSelector/waitForSelector.js +0 -1
  18. package/hooks/build +3 -0
  19. package/lib/browser.js +81 -33
  20. package/lib/index.js +18 -9
  21. package/lib/loader.js +3 -4
  22. package/lib/metadata/metadata.json +180 -29
  23. package/modules/ajaxRequests/scope.js +1 -1
  24. package/modules/analyzeCss/analyzeCss.js +79 -76
  25. package/modules/analyzeCss/scope.js +1 -1
  26. package/modules/blockDomains/blockDomains.js +21 -21
  27. package/modules/cacheHits/cacheHits.js +6 -3
  28. package/modules/cpuTasks/cpuTasks.js +22 -0
  29. package/modules/documentHeight/scope.js +1 -1
  30. package/modules/domComplexity/scope.js +1 -1
  31. package/modules/domHiddenContent/scope.js +1 -1
  32. package/modules/domMutations/scope.js +1 -1
  33. package/modules/domQueries/domQueries.js +16 -19
  34. package/modules/domQueries/scope.js +1 -1
  35. package/modules/domains/domains.js +1 -1
  36. package/modules/events/scope.js +2 -1
  37. package/modules/globalVariables/scope.js +1 -1
  38. package/modules/jQuery/scope.js +1 -1
  39. package/modules/javaScriptBottlenecks/scope.js +1 -1
  40. package/modules/lazyLoadableImages/scope.js +2 -2
  41. package/modules/localStorage/scope.js +1 -1
  42. package/modules/protocols/protocols.js +101 -0
  43. package/modules/requestsStats/requestsStats.js +1 -1
  44. package/modules/staticAssets/staticAssets.js +2 -1
  45. package/modules/windowPerformance/windowPerformance.js +1 -0
  46. package/package.json +31 -20
  47. package/lib/fast-stats.js +0 -634
  48. package/lib/metadata/generate.js +0 -283
  49. package/lib/metadata/make_docs.js +0 -185
  50. package/reporters/csv.js +0 -54
  51. package/reporters/plain.js +0 -173
  52. package/reporters/statsd.js +0 -82
  53. package/reporters/tap.js +0 -71
  54. package/reporters/teamcity.js +0 -73
@@ -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
- };
@@ -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
- };