phantomas 2.2.0 → 2.5.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 CHANGED
@@ -1,26 +1,30 @@
1
1
  # https://hub.docker.com/_/node
2
- FROM node:lts-alpine3.13
3
-
4
- # Installs latest Chromium package.
5
- # https://pkgs.alpinelinux.org/package/edge/community/x86_64/chromium
6
- ENV CHROMIUM_VERSION 88.0.4324.182-r0
7
-
8
- RUN echo "http://dl-cdn.alpinelinux.org/alpine/edge/main" > /etc/apk/repositories \
9
- && echo "http://dl-cdn.alpinelinux.org/alpine/edge/community" >> /etc/apk/repositories \
10
- && echo "http://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories \
11
- && echo "http://dl-cdn.alpinelinux.org/alpine/v3.13/main" >> /etc/apk/repositories \
12
- && apk upgrade -U -a \
13
- && apk add \
14
- chromium \
15
- ca-certificates \
16
- freetype \
17
- freetype-dev \
18
- harfbuzz \
19
- nss \
20
- ttf-freefont
21
-
22
- RUN which chromium-browser
23
- RUN chromium-browser --no-sandbox --version
2
+ FROM node:lts-bullseye-slim
3
+
4
+ # install dependencies of Chrome binary that will be fetched by npm ci
5
+ RUN apt-get update \
6
+ && apt-get install -y --no-install-recommends \
7
+ fonts-liberation \
8
+ libasound2 \
9
+ libatk-bridge2.0-0 \
10
+ libatk1.0-0 \
11
+ libatspi2.0-0 \
12
+ libc6 \
13
+ libcairo2 \
14
+ libcups2 \
15
+ libdbus-1-3 \
16
+ libfreetype6 \
17
+ libgbm1 \
18
+ libharfbuzz0b \
19
+ libnss3 \
20
+ libpango-1.0-0 \
21
+ libx11-6 \
22
+ libxext6 \
23
+ libxkbcommon0 \
24
+ x11-utils \
25
+ xdg-utils \
26
+ zlib1g \
27
+ && rm -rf /var/lib/apt/lists/*
24
28
 
25
29
  # Set up a working directory
26
30
  ENV HOME /opt/phantomas
@@ -30,11 +34,6 @@ RUN chown -R nobody:nogroup .
30
34
  # Run everything after as non-privileged user.
31
35
  USER nobody
32
36
 
33
- # Tell Puppeteer to skip installing Chrome. We'll be using the installed binary
34
- ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD true
35
-
36
- # Tell phantomas where Chromium binary is and that we're in docker
37
- ENV PHANTOMAS_CHROMIUM_EXECUTABLE /usr/bin/chromium-browser
38
37
  ENV DOCKERIZED yes
39
38
 
40
39
  # Install dependencies
@@ -42,8 +41,12 @@ COPY package.json .
42
41
  COPY package-lock.json .
43
42
  RUN npm ci
44
43
 
45
- ARG COMMIT_SHA="dev"
46
- ENV COMMIT_SHA ${COMMIT_SHA}
44
+ # TODO: find the chrome binary and symlink it to the PATH
45
+ RUN ldd $(find . -wholename '*chrome-linux/chrome') && \
46
+ $(find . -wholename '*chrome-linux/chrome') --version
47
+
48
+ ARG GITHUB_SHA="dev"
49
+ ENV COMMIT_SHA ${GITHUB_SHA}
47
50
 
48
51
  # label the image with branch name and commit hash
49
52
  LABEL maintainer="maciej.brencz@gmail.com"
package/README.md CHANGED
@@ -1,11 +1,16 @@
1
- phantomas [![npm](https://img.shields.io/npm/dt/phantomas.svg)]() [![Inline docs](http://inch-ci.org/github/macbre/phantomas.svg?branch=phantomas-v2)](http://inch-ci.org/github/macbre/phantomas) [![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier)
1
+ phantomas
2
+ [![npm](https://img.shields.io/npm/dt/phantomas.svg)]()
3
+ [![Inline docs](http://inch-ci.org/github/macbre/phantomas.svg?branch=phantomas-v2)](http://inch-ci.org/github/macbre/phantomas)
4
+ [![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier)
5
+ [![Coverage Status](https://coveralls.io/repos/github/macbre/phantomas/badge.svg?branch=devel&1)](https://coveralls.io/github/macbre/phantomas?branch=devel)
6
+ [![CodeFactor](https://www.codefactor.io/repository/github/macbre/phantomas/badge)](https://www.codefactor.io/repository/github/macbre/phantomas)
2
7
  =========
3
8
 
4
9
  [Headless Chromium](https://chromium.googlesource.com/chromium/src/+/lkgr/headless/README.md)-based modular web performance metrics collector. And why phantomas? Well, [because](http://en.wikipedia.org/wiki/Fantômas) :)
5
10
 
6
11
  ## Requirements
7
12
 
8
- * [NodeJS](http://nodejs.org) 12+
13
+ * [NodeJS](http://nodejs.org) 14+
9
14
 
10
15
  ## Installation
11
16
 
@@ -27,6 +32,12 @@ You can use [phantomas Docker image](https://hub.docker.com/r/macbre/phantomas):
27
32
  docker pull macbre/phantomas:latest
28
33
  ```
29
34
 
35
+ Or you can fetch from [GitHub's Containers registry](https://github.com/macbre/phantomas/pkgs/container/phantomas)
36
+
37
+ ```
38
+ docker pull ghcr.io/macbre/phantomas:latest
39
+ ```
40
+
30
41
  ## Support
31
42
 
32
43
  [![Foo](https://raw.githubusercontent.com/macbre/phantomas/devel/docs/phantomas-banner.png)](https://xscode.com/macbre/phantomas)
package/bin/program.js CHANGED
@@ -68,6 +68,14 @@ function getProgram() {
68
68
  "--cookies-file <file>",
69
69
  "specifies the file name to store the persistent Cookies"
70
70
  )
71
+ .option(
72
+ "--local-storage <values>",
73
+ 'ability to set a local storage, key-value pairs (e.g. "bar=foo;domain=url")'
74
+ )
75
+ .option(
76
+ "--session-storage <values>",
77
+ 'ability to set a session storage, key-value pairs (e.g. "bar=foo;domain=url")'
78
+ )
71
79
  .option(
72
80
  "--ignore-ssl-errors",
73
81
  "ignores SSL errors, such as expired or self-signed certificate errors"
@@ -1,4 +1,5 @@
1
- (function (phantomas) {
1
+ /* istanbul ignore next */
2
+ (function navigationTimingScope(phantomas) {
2
3
  // Prevent events from being sent from an iframe. Only from the main document.
3
4
  if (window.parent !== window || window.location.href === "about:blank") {
4
5
  return;
package/core/scope.js CHANGED
@@ -3,7 +3,8 @@
3
3
  *
4
4
  * Code below is executed in page's "scope" (injected by lib/browser.js)
5
5
  */
6
- (function (scope) {
6
+ /* istanbul ignore next */
7
+ (function coreScope(scope) {
7
8
  "use strict";
8
9
 
9
10
  // create a scope
@@ -33,6 +33,8 @@ module.exports = (phantomas) => {
33
33
  return new Promise(async (resolve) => {
34
34
  // https://github.com/GoogleChrome/puppeteer/blob/v1.11.0/docs/api.md#pageevaluatepagefunction-args
35
35
  const bodyHandle = await page.$("body");
36
+
37
+ /* istanbul ignore next */
36
38
  const html = await page.evaluate((body) => body.innerHTML, bodyHandle);
37
39
 
38
40
  // phantomas.log(html);
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Support for session-/localStorage injection.
3
+ */
4
+ "use strict";
5
+
6
+ module.exports = function (phantomas) {
7
+ const SESSION_STORAGE = "session-storage",
8
+ LOCAL_STORAGE = "local-storage";
9
+
10
+ phantomas.on("init", async (page) => {
11
+ let sessionStorage = phantomas.getParam(SESSION_STORAGE, false);
12
+ let localStorage = phantomas.getParam(LOCAL_STORAGE, false);
13
+
14
+ // Mapping given "storage-string" to json object if string has been given
15
+ if (typeof sessionStorage == "string" || sessionStorage instanceof String) {
16
+ sessionStorage = parseStorage(sessionStorage);
17
+ }
18
+
19
+ if (typeof localStorage == "string" || localStorage instanceof String) {
20
+ localStorage = parseStorage(localStorage);
21
+ }
22
+
23
+ if (sessionStorage) {
24
+ phantomas.log(
25
+ "Injecting sessionStorage: %j",
26
+ JSON.stringify(sessionStorage)
27
+ );
28
+ await injectStorage(page, sessionStorage, SESSION_STORAGE);
29
+ }
30
+ if (localStorage) {
31
+ phantomas.log("Injecting localStorag: %j", JSON.stringify(localStorage));
32
+ await injectStorage(page, localStorage, LOCAL_STORAGE);
33
+ }
34
+ });
35
+
36
+ function parseStorage(storageString) {
37
+ // --sessionStorage='bar=foo;domain=url'
38
+ // --localStorage='bar=fooLocal;domain=urlLocal'
39
+ var storageMap = {};
40
+ storageString.split(";").forEach(function (singleEntry) {
41
+ var entryKeyValue = singleEntry.split("=");
42
+ storageMap[entryKeyValue[0]] = entryKeyValue[1];
43
+ });
44
+ return storageMap;
45
+ }
46
+
47
+ /**
48
+ * Inject the given storage into the specified page storage.
49
+ * Either localStorage or sessionStorage
50
+ *
51
+ * @param {Page} page in which page the storage should be injected
52
+ * @param {Object} storage the JSON object consisting of the storage keys and values
53
+ * @param {string} storageType either localStorage or sessionStorage
54
+ */
55
+ async function injectStorage(page, storage, storageType) {
56
+ if (!page || !storage || !storageType) {
57
+ return;
58
+ }
59
+
60
+ /* istanbul ignore next */
61
+ await page.evaluateOnNewDocument(
62
+ (storage, storageType, SESSION_STORAGE, LOCAL_STORAGE) => {
63
+ const keys = Object.keys(storage);
64
+ const values = Object.values(storage);
65
+ if (storageType === SESSION_STORAGE) {
66
+ for (let i = 0; i < keys.length; i++) {
67
+ sessionStorage.setItem(keys[i], values[i]);
68
+ }
69
+ }
70
+ if (storageType === LOCAL_STORAGE) {
71
+ for (let i = 0; i < keys.length; i++) {
72
+ localStorage.setItem(keys[i], values[i]);
73
+ }
74
+ }
75
+ },
76
+ storage,
77
+ storageType,
78
+ SESSION_STORAGE,
79
+ LOCAL_STORAGE
80
+ );
81
+ }
82
+ };
@@ -21,7 +21,10 @@ module.exports = function (phantomas) {
21
21
  return new Promise(async (resolve) => {
22
22
  phantomas.log("Scrolling the page...");
23
23
 
24
+ /* istanbul ignore next */
24
25
  await page.evaluate(() => document.body.scrollIntoView(false));
26
+
27
+ /* istanbul ignore next */
25
28
  const scrollOffset = await page.evaluate(() => document.body.scrollTop);
26
29
 
27
30
  // wait for lazy loading to do its job
package/hooks/build CHANGED
@@ -1,3 +1,3 @@
1
1
  #!/bin/bash
2
2
  # see https://docs.docker.com/docker-hub/builds/advanced/#build-hook-examples
3
- docker build --build-arg COMMIT_SHA=$SOURCE_COMMIT -f $DOCKERFILE_PATH -t $IMAGE_NAME .
3
+ docker build --build-arg GITHUB_SHA=$SOURCE_COMMIT -f $DOCKERFILE_PATH -t $IMAGE_NAME .
package/lib/browser.js CHANGED
@@ -1,3 +1,4 @@
1
+ // @ts-check
1
2
  /**
2
3
  * Expose puppeteer API and events emitter object for lib/index.js
3
4
  */
@@ -11,7 +12,7 @@ function Browser() {
11
12
 
12
13
  /**
13
14
  * Use the provided events emitter
14
- * @param {EventEmitter} events
15
+ * @param {puppeteer.EventEmitter} events
15
16
  */
16
17
  Browser.prototype.bind = (events) => (this.events = events);
17
18
 
@@ -36,6 +37,7 @@ Browser.prototype.init = async (phantomasOptions) => {
36
37
  }
37
38
 
38
39
  // customize path to Chromium binary
40
+ /* istanbul ignore next */
39
41
  if (env["PHANTOMAS_CHROMIUM_EXECUTABLE"]) {
40
42
  options.executablePath = env["PHANTOMAS_CHROMIUM_EXECUTABLE"];
41
43
  }
@@ -43,6 +45,7 @@ Browser.prototype.init = async (phantomasOptions) => {
43
45
  // detect that we run inside a container
44
46
  // @see https://github.com/jessfraz/dockerfiles/issues/65
45
47
  // @see https://github.com/Zenika/alpine-chrome#-the-best-with-seccomp
48
+ /* istanbul ignore next */
46
49
  if (env["DOCKERIZED"]) {
47
50
  debug("Docker environment detected");
48
51
  debug(
@@ -53,6 +56,7 @@ Browser.prototype.init = async (phantomasOptions) => {
53
56
  // detect that we run inside an Amazon Lambda machine
54
57
  // (note: the LAMBDA_TASK_ROOT env variable is automatically set by AWS)
55
58
  // @see https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html#configuration-envvars-runtime
59
+ /* istanbul ignore next */
56
60
  if (env["LAMBDA_TASK_ROOT"]) {
57
61
  // Chrome then requires some more flags
58
62
  options.args.push(
@@ -69,13 +73,13 @@ Browser.prototype.init = async (phantomasOptions) => {
69
73
  // https://github.com/puppeteer/puppeteer/blob/main/docs/api.md#puppeteerlaunchoptions
70
74
  this.browser = await puppeteer.launch(options);
71
75
  this.page = await this.browser.newPage();
72
- } catch (ex) {
76
+ } catch (ex) /* istanbul ignore next */ {
73
77
  debug("Puppeteer failed to launch: %s", ex);
74
78
  throw ex;
75
79
  }
76
80
 
77
81
  // A Chrome Devtools Protocol session attached to the target
78
- this.cdp = this.page._client;
82
+ this.cdp = this.page._client();
79
83
 
80
84
  debug("Using binary from: %s", this.browser.process().spawnfile);
81
85
 
@@ -94,13 +98,14 @@ Browser.prototype.init = async (phantomasOptions) => {
94
98
  this.page.on("dialog", async (dialog) => {
95
99
  // https://github.com/GoogleChrome/puppeteer/blob/v1.11.0/docs/api.md#class-dialog
96
100
  const message = dialog.message();
97
- debug("dialog: %s [%s]", dialog._type, message);
101
+ const type = dialog.type();
102
+ debug("dialog: %s [%s]", type, message);
98
103
 
99
- switch (dialog._type) {
104
+ switch (type) {
100
105
  case "alert":
101
106
  case "confirm":
102
107
  case "prompt":
103
- this.events.emit(dialog._type, message); // @desc Emitted when a JavaScript dialog appears: alert, prompt or confirm
108
+ this.events.emit(type, message); // @desc Emitted when a JavaScript dialog appears: alert, prompt or confirm
104
109
  break;
105
110
  }
106
111
 
@@ -167,6 +172,7 @@ Browser.prototype.init = async (phantomasOptions) => {
167
172
  this.onRequestLoaded = (eventName, data) => {
168
173
  var meta = responses[data.requestId];
169
174
 
175
+ /* istanbul ignore if */
170
176
  if (typeof meta === "undefined") {
171
177
  // the browser sometimes looses trace of a request, let's ignore.
172
178
  networkDebug(
@@ -307,7 +313,7 @@ Browser.prototype.visit = (url, waitUntil, timeout) => {
307
313
  debug("Metrics: %s", JSON.stringify(metrics));
308
314
 
309
315
  this.events.emit("metrics", metrics); // @desc Emitted when Chromuim's page.metrics() has been called
310
- } catch (ex) {
316
+ } catch (ex) /* istanbul ignore next */ {
311
317
  debug("Get metrics failed: " + ex);
312
318
  return reject(ex);
313
319
  }
@@ -325,7 +331,7 @@ Browser.prototype.close = async () => {
325
331
 
326
332
  // The page is closed, let's close the browser
327
333
  if (this.browser && this.browser.isConnected()) await this.browser.close();
328
- } catch (ex) {
334
+ } catch (ex) /* istanbul ignore next */ {
329
335
  debug("An exception was raised in Browser.prototype.close(): " + ex);
330
336
  debug(ex);
331
337
  }
package/lib/index.js CHANGED
@@ -29,7 +29,7 @@ function phantomas(url, opts) {
29
29
  debug("phantomas: %s", VERSION);
30
30
  debug(
31
31
  "Puppeteer: preferred revision r%s",
32
- puppeteer._launcher._preferredRevision
32
+ puppeteer.default._preferredRevision
33
33
  );
34
34
  debug("URL: <%s>", url);
35
35
 
@@ -39,7 +39,7 @@ function phantomas(url, opts) {
39
39
 
40
40
  debug("Options: %s", JSON.stringify(options));
41
41
 
42
- events.setMaxListeners(100); // MaxListenersExceededWarning: Possible EventEmitter memory leak detected.
42
+ events.setMaxListeners(250); // MaxListenersExceededWarning: Possible EventEmitter memory leak detected.
43
43
 
44
44
  var results = new Results();
45
45
  results.setUrl(url);
@@ -82,7 +82,9 @@ function phantomas(url, opts) {
82
82
  evaluate: page.evaluate.bind(page),
83
83
 
84
84
  // @see https://github.com/GoogleChrome/puppeteer/blob/v1.11.0/docs/api.md#pageselector-1
85
- querySelectorAll: async (selector) => {
85
+ querySelectorAll: async function querySelectorAll(
86
+ selector
87
+ ) /* istanbul ignore next */ {
86
88
  debug('querySelectorAll("%s")', selector);
87
89
  return page.$$(selector);
88
90
  },
@@ -106,6 +108,7 @@ function phantomas(url, opts) {
106
108
 
107
109
  // pass phantomas options to page scope
108
110
  // https://github.com/GoogleChrome/puppeteer/blob/v1.11.0/docs/api.md#pageevaluateonnewdocumentpagefunction-args
111
+ /* istanbul ignore next */
109
112
  await page.evaluateOnNewDocument((options) => {
110
113
  window.__phantomas_options = options;
111
114
  }, options);
@@ -132,6 +135,7 @@ function phantomas(url, opts) {
132
135
  scope[type].apply(scope, args);
133
136
  break;
134
137
 
138
+ /* istanbul ignore next */
135
139
  default:
136
140
  debug("Unrecognized event type: " + type);
137
141
  }
@@ -63,7 +63,7 @@
63
63
  "init": {
64
64
  "file": "/lib/index.js",
65
65
  "desc": "Browser's scope and modules are set up, the page is about to be loaded",
66
- "arguments": "page, browser"
66
+ "arguments": "page, browser.getPuppeteerBrowser("
67
67
  },
68
68
  "beforeClose": {
69
69
  "file": "/lib/index.js",
@@ -102,6 +102,11 @@
102
102
  "desc": "Saves the source of page being loaded to a file\nPlease note that saving each file takes a few ms.\nConsider increasing default timeout.\nRun phantomas with --page-source option to use this module.",
103
103
  "options": []
104
104
  },
105
+ "pageStorage": {
106
+ "file": "/extensions/pageStorage/pageStorage.js",
107
+ "desc": "",
108
+ "options": []
109
+ },
105
110
  "postLoadDelay": {
106
111
  "file": "/extensions/postLoadDelay/postLoadDelay.js",
107
112
  "desc": "Delays report generation for a given time",
@@ -117,6 +122,11 @@
117
122
  "desc": "Allow page to be scrolled after it is loaded\nPass --scroll as an option in CLI mode",
118
123
  "options": []
119
124
  },
125
+ "userAgent": {
126
+ "file": "/extensions/userAgent/userAgent.js",
127
+ "desc": "Sets a user agent according to --user-agent or --phone or --tablet options",
128
+ "options": []
129
+ },
120
130
  "viewport": {
121
131
  "file": "/extensions/viewport/viewport.js",
122
132
  "desc": "Provides the --viewport option to set any device resolution and pixel density ratio.\nIf the user sets a viewport size as well as a device option (--phone, --tablet, ...),\nwe assume that he or she wants to overwrite the device values.\nTwo syntaxes are supported:\n - 1200x800 will set a 1x pixel density ratio\n - 1200x800x2 will set the given ratio (float values such as 1.5 are accepted)",
@@ -1050,13 +1060,13 @@
1050
1060
  "offenders": true,
1051
1061
  "unit": "number",
1052
1062
  "module": "assetsTypes",
1053
- "testsCovered": false
1063
+ "testsCovered": true
1054
1064
  },
1055
1065
  "videoSize": {
1056
1066
  "desc": "size of video responses (with compression)",
1057
1067
  "unit": "bytes",
1058
1068
  "module": "assetsTypes",
1059
- "testsCovered": false
1069
+ "testsCovered": true
1060
1070
  },
1061
1071
  "base64Count": {
1062
1072
  "desc": "number of base64 encoded \"responses\" (no HTTP request was actually made)",
@@ -1206,7 +1216,7 @@
1206
1216
  "testsCovered": false
1207
1217
  },
1208
1218
  "recalcStyleDuration": {
1209
- "desc": "combined duration of all page style recalculations",
1219
+ "desc": "combined duration of style recalculations",
1210
1220
  "unit": "ms",
1211
1221
  "module": "cpuTasks",
1212
1222
  "testsCovered": false
@@ -1620,7 +1630,7 @@
1620
1630
  "testsCovered": true
1621
1631
  },
1622
1632
  "oldTlsProtocol": {
1623
- "desc": "number of domains using TLS 1.1 or 1.2",
1633
+ "desc": "number of domains using TLS 1.2",
1624
1634
  "offenders": true,
1625
1635
  "unit": "number",
1626
1636
  "module": "protocols",
@@ -1868,6 +1878,6 @@
1868
1878
  },
1869
1879
  "metricsCount": 187,
1870
1880
  "modulesCount": 36,
1871
- "extensionsCount": 12,
1872
- "version": "2.0.0"
1881
+ "extensionsCount": 14,
1882
+ "version": "2.4.0"
1873
1883
  }
@@ -1,4 +1,4 @@
1
- (function (phantomas) {
1
+ (function ajaxRequestsScope(phantomas) {
2
2
  phantomas.spy(
3
3
  window.XMLHttpRequest.prototype,
4
4
  "open",