From e6232f494ba2fb43a25882dfdbab9b9e0263f9bc Mon Sep 17 00:00:00 2001 From: mgmarlow Date: Mon, 27 Nov 2017 13:41:25 -0800 Subject: [PATCH 01/19] add initial script with configuration options and command line parsing --- package-lock.json | 347 +++++++++++++++++++ package.json | 6 +- src/cli/shared/parser.ts | 26 ++ src/cli/verify-sandboxes/verify-sandboxes.ts | 159 +++++++++ tsconfig.json | 2 +- tslint.json | 2 +- 6 files changed, 539 insertions(+), 3 deletions(-) create mode 100644 src/cli/shared/parser.ts create mode 100644 src/cli/verify-sandboxes/verify-sandboxes.ts diff --git a/package-lock.json b/package-lock.json index 446eda33..2ff8ea78 100644 --- a/package-lock.json +++ b/package-lock.json @@ -76,6 +76,24 @@ "integrity": "sha1-5+E068Z0rm7ZPDbHZ3ObEQ0sV/w=", "dev": true }, + "@types/puppeteer": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/@types/puppeteer/-/puppeteer-0.13.3.tgz", + "integrity": "sha512-QPzng6tTcWPnjG51EATK74TRz5dtst8BqXdZt9QgbZAaY+2bq0t6m5Mj3hJ8hJV9UFlXOq3B6PYBwWIh7qZX5Q==", + "dev": true, + "requires": { + "@types/node": "6.0.60" + } + }, + "agent-base": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.1.2.tgz", + "integrity": "sha512-VE6QoEdaugY86BohRtfGmTDabxdU5sCKOkbcPA6PXKJsRzEi/7A3RCTxJal1ft/4qSfPht5/iQLhMh/wzSkkNw==", + "dev": true, + "requires": { + "es6-promisify": "5.0.0" + } + }, "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", @@ -155,6 +173,12 @@ "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", "dev": true }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + }, "ast-types": { "version": "0.9.6", "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.9.6.tgz", @@ -184,6 +208,12 @@ "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", "dev": true }, + "async-limiter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", + "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==", + "dev": true + }, "babel-code-frame": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", @@ -289,6 +319,21 @@ "sprintf-js": "1.1.1" } }, + "color-convert": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", + "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, "colors": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", @@ -301,6 +346,49 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, + "concat-stream": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz", + "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=", + "dev": true, + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.3.3", + "typedarray": "0.0.6" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "readable-stream": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + } + } + }, "copyfiles": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/copyfiles/-/copyfiles-1.2.0.tgz", @@ -353,6 +441,15 @@ "integrity": "sha1-QGXiATz5+5Ft39gu+1Bq1MZ2kGI=", "dev": true }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, "defaults": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", @@ -426,6 +523,21 @@ } } }, + "es6-promise": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.1.1.tgz", + "integrity": "sha512-OaU1hHjgJf+b0NzsxCg7NdIYERD6Hy/PEmFLTjw+b65scuisG3Kt4QoTvJ66BBkPZ581gr0kpoVzKnxniM8nng==", + "dev": true + }, + "es6-promisify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", + "dev": true, + "requires": { + "es6-promise": "4.1.1" + } + }, "es6-templates": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/es6-templates/-/es6-templates-0.2.3.tgz", @@ -496,6 +608,35 @@ "is-extglob": "1.0.0" } }, + "extract-zip": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.6.tgz", + "integrity": "sha1-EpDt6NINCHK0Kf0/NRyhKOxe+Fw=", + "dev": true, + "requires": { + "concat-stream": "1.6.0", + "debug": "2.6.9", + "mkdirp": "0.5.0", + "yauzl": "2.4.1" + }, + "dependencies": { + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "mkdirp": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz", + "integrity": "sha1-HXMHam35hs2TROFecfzAWkyavxI=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + } + } + }, "fancy-log": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.0.tgz", @@ -512,6 +653,15 @@ "integrity": "sha1-0eJkOzipTXWDtHkGDmxK/8lAcfg=", "dev": true }, + "fd-slicer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz", + "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=", + "dev": true, + "requires": { + "pend": "1.2.0" + } + }, "filename-regex": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", @@ -901,6 +1051,12 @@ "ansi-regex": "2.1.1" } }, + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "dev": true + }, "has-gulplog": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/has-gulplog/-/has-gulplog-0.1.0.tgz", @@ -919,6 +1075,16 @@ "parse-passwd": "1.0.0" } }, + "https-proxy-agent": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.1.0.tgz", + "integrity": "sha512-/DTVSUCbRc6AiyOV4DBRvPDpKKCJh4qQJNaCgypX0T41quD9hp/PB5iUyx/60XobuMPQa9ce1jNV9UOUq6PnTg==", + "dev": true, + "requires": { + "agent-base": "4.1.2", + "debug": "2.6.9" + } + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -1307,6 +1473,12 @@ "integrity": "sha1-Wrh60dTB2rjowIu/A37gwZAih88=", "dev": true }, + "make-error": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.0.tgz", + "integrity": "sha1-Uq06M5zPEM5itAQLcI/nByRLi5Y=", + "dev": true + }, "map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", @@ -1334,6 +1506,12 @@ "regex-cache": "0.4.4" } }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -1366,6 +1544,12 @@ } } }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, "multipipe": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/multipipe/-/multipipe-0.1.2.tgz", @@ -1573,6 +1757,12 @@ "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=", "dev": true }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", + "dev": true + }, "preserve": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", @@ -1597,6 +1787,34 @@ "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", "dev": true }, + "progress": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz", + "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=", + "dev": true + }, + "proxy-from-env": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz", + "integrity": "sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4=", + "dev": true + }, + "puppeteer": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-0.13.0.tgz", + "integrity": "sha512-M52SA/WmW54YMLzFtCLGslhr9tntzfTgJIZnx3QnaDXn9F5q2BlTosywSBEKj8aVVd6al0WNfiu14MUQW3wjaw==", + "dev": true, + "requires": { + "debug": "2.6.9", + "extract-zip": "1.6.6", + "https-proxy-agent": "2.1.0", + "mime": "1.6.0", + "progress": "2.0.0", + "proxy-from-env": "1.0.0", + "rimraf": "2.6.2", + "ws": "3.3.2" + } + }, "randomatic": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", @@ -1805,6 +2023,15 @@ "global-modules": "0.2.3" } }, + "rimraf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "dev": true, + "requires": { + "glob": "7.1.2" + } + }, "rxjs": { "version": "5.5.2", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.2.tgz", @@ -1911,6 +2138,12 @@ "is-utf8": "0.2.1" } }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + }, "supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", @@ -1986,6 +2219,82 @@ "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=", "dev": true }, + "ts-node": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-3.3.0.tgz", + "integrity": "sha1-wTxqMCTjC+EYDdUwOPwgkonUv2k=", + "dev": true, + "requires": { + "arrify": "1.0.1", + "chalk": "2.3.0", + "diff": "3.3.1", + "make-error": "1.3.0", + "minimist": "1.2.0", + "mkdirp": "0.5.1", + "source-map-support": "0.4.18", + "tsconfig": "6.0.0", + "v8flags": "3.0.1", + "yn": "2.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", + "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", + "dev": true, + "requires": { + "color-convert": "1.9.1" + } + }, + "chalk": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", + "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", + "dev": true, + "requires": { + "ansi-styles": "3.2.0", + "escape-string-regexp": "1.0.5", + "supports-color": "4.5.0" + } + }, + "supports-color": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", + "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "dev": true, + "requires": { + "has-flag": "2.0.0" + } + }, + "v8flags": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.0.1.tgz", + "integrity": "sha1-3Oj8N5wX2fLJ6e142JzgAFKxt2s=", + "dev": true, + "requires": { + "homedir-polyfill": "1.0.1" + } + } + } + }, + "tsconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-6.0.0.tgz", + "integrity": "sha1-aw6DdgA9evGGT434+J3QBZ/80DI=", + "dev": true, + "requires": { + "strip-bom": "3.0.0", + "strip-json-comments": "2.0.1" + }, + "dependencies": { + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + } + } + }, "tsickle": { "version": "0.24.1", "resolved": "https://registry.npmjs.org/tsickle/-/tsickle-0.24.1.tgz", @@ -2030,12 +2339,24 @@ "tslib": "1.7.1" } }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, "typescript": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.4.2.tgz", "integrity": "sha1-+DlfhdRZJ2BnyYiqQYN6j4KHCEQ=", "dev": true }, + "ultron": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", + "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==", + "dev": true + }, "unc-path-regex": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", @@ -2160,12 +2481,38 @@ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, + "ws": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.2.tgz", + "integrity": "sha512-t+WGpsNxhMR4v6EClXS8r8km5ZljKJzyGhJf7goJz9k5Ye3+b5Bvno5rjqPuIBn5mnn5GBb7o8IrIWHxX1qOLQ==", + "dev": true, + "requires": { + "async-limiter": "1.0.0", + "safe-buffer": "5.1.1", + "ultron": "1.1.1" + } + }, "xtend": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", "dev": true }, + "yauzl": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz", + "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=", + "dev": true, + "requires": { + "fd-slicer": "1.0.1" + } + }, + "yn": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz", + "integrity": "sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=", + "dev": true + }, "zone.js": { "version": "0.8.4", "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.8.4.tgz", diff --git a/package.json b/package.json index c4968032..175891af 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,8 @@ "build": "npm run app:build && npm run cli:build", "app:build": "gulp build", "cli:build": "tsc -p ./src/cli/tsconfig.json", - "prepublish": "npm run build" + "prepublish": "npm run build", + "test": "ts-node ./src/cli/verify-sandboxes/verify-sandboxes.ts --path=./examples/example-app-angular-cli/src/sandboxes.ts --build" }, "repository": { "type": "git", @@ -56,13 +57,16 @@ "@angular/platform-browser": "^5.0.0", "@angular/platform-browser-dynamic": "^5.0.0", "@types/node": "6.0.60", + "@types/puppeteer": "^0.13.3", "codelyzer": "3.0.1", "copyfiles": "^1.2.0", "fs-extra": "^4.0.1", "glob": "^7.1.2", "gulp": "^3.9.1", "gulp-inline-ng2-template": "^4.0.0", + "puppeteer": "^0.13.0", "rxjs": "^5.5.2", + "ts-node": "^3.3.0", "tslint": "5.3.2", "typescript": "2.4.2", "zone.js": "0.8.4" diff --git a/src/cli/shared/parser.ts b/src/cli/shared/parser.ts new file mode 100644 index 00000000..77115b8c --- /dev/null +++ b/src/cli/shared/parser.ts @@ -0,0 +1,26 @@ +/** + * Separates accepted command line arguments from other ts-node arguments + * @param supportedFlags - Accepted command line flags + * @param args - Process arguments + */ +export function getParsedArguments(supportedFlags: string[], args: string[]) + : { flags: string[], args: string[] } { + const flags: string[] = []; + + args = args.reduce((accr, value) => { + const flag = getArgumentValue(value); + supportedFlags.indexOf(flag) > -1 ? flags.push(value) : accr.push(value); + return accr; + }, []); + + return { flags, args }; +} + +/** + * Only grab flag from arguments with values + * e.g. --path=./src/ + * @param argument - Flag argument + */ +export function getArgumentValue(argument: string): string { + return argument.split('=')[0]; +} diff --git a/src/cli/verify-sandboxes/verify-sandboxes.ts b/src/cli/verify-sandboxes/verify-sandboxes.ts new file mode 100644 index 00000000..9548ffc1 --- /dev/null +++ b/src/cli/verify-sandboxes/verify-sandboxes.ts @@ -0,0 +1,159 @@ +import * as puppeteer from 'puppeteer'; +import * as process from 'process'; +import { getParsedArguments, getArgumentValue } from '../shared/parser'; + +// TODO: Update Docs! + +class Configuration { + constructor ( + public path: string, + public build: boolean, + public port: number, + ) {} + + get baseUrl(): string { + return `http://localhost:${this.port}`; + } +} + +// Parse command line input +const supportedFlages = ['--path', '--build', '--port']; +const parameters = process.argv; +const parsedArguments = getParsedArguments(supportedFlages, parameters); + +const chromeArguments = [ + '--disable-gpu', + '--no-sandbox' +]; + +let browser: any; +let currentScenario = ''; + +// Ensure Chromium instances are destroyed on err +process.on('unhandledRejection', () => { + if (browser) browser.close(); +}); + + +// const cmd_args = process.argv.filter(arg => arg.includes('--')); +// const sandboxesPath = cmd_args[0].substring('--path='.length, cmd_args[0].length); +// const selectRandomScenario = cmd_args[1] === '--build'; + + +// const BASE_URL = 'http://localhost:8002/'; +// let currentScenario = ''; +// let browser; + +// // Ensure Chromium instances are destroyed +// process.on('unhandledRejection', () => { +// if (browser) { +// browser.close(); +// } +// }); + +(async () => { + const configuration = configure(parsedArguments.flags); + // await main(); +})(); + + +///////////////////////////////// + +// interface ScenarioSummary { +// url: string; +// name: string; +// description: string; +// } + +// async function main () { +// browser = await puppeteer.launch({ +// headless: true, +// handleSIGINT: false, +// args +// }); + +// const scenarios = getSandboxMetadata(selectRandomScenario); +// console.log(`Retrieved ${scenarios.length} scenarios.\n`); +// for (let i = 0; i < scenarios.length; i++) { +// await work(scenarios[i]); +// } + +// browser.close(); +// } + +function configure(flags: string[]): Configuration { + const pathArg = flags.find(f => f.includes('--path')); + const portArg = flags.find(f => f.includes('--port')); + + if (!pathArg) { + console.error('Please specify the path to sandboxes.ts'); + process.exit(1); + } + + const path = getArgumentValue(pathArg); + const buildMode = flags.indexOf('--build') !== -1; + const port = portArg ? parseInt(getArgumentValue(portArg), 10) : 8002; + + return new Configuration(path, buildMode, port); +} + +// function getSandboxMetadata(randomScenario) { +// const scenarios: ScenarioSummary[] = []; + +// loadSandboxMenuItems(sandboxesPath).forEach(scenario => { +// if (randomScenario) { +// // Pick a random scenario for each component +// const randomItemKey = getRandomKey(scenario.scenarioMenuItems.length); +// scenario.scenarioMenuItems +// .forEach(item => { +// if (item.key === randomItemKey) { +// const url = `${BASE_URL}?scenario=${encodeURIComponent(scenario.key)}/${encodeURIComponent(item.description)}`; +// scenarios.push({ url, name: scenario.key, description: item.description }); +// } +// }); +// } else { +// // Grab all scenarios +// scenario.scenarioMenuItems +// .forEach(item => { +// const url = `${BASE_URL}?scenario=${encodeURIComponent(scenario.key)}/${encodeURIComponent(item.description)}`; +// scenarios.push({ url, name: scenario.key, description: item.description }); +// }); +// } +// }); + +// return scenarios; +// } + +// function loadSandboxMenuItems(path: string) { +// try { +// return require(path).getSandboxMenuItems(); +// } catch (err) { +// console.error('Failed to load sandboxes.ts file.'); +// console.error(err); +// console.log('Terminating process.'); +// process.exit(1); +// } +// } + +// async function work (scenario) { +// const page = await browser.newPage(); +// page.on('console', msg => onConsoleErr(msg)); +// currentScenario = scenario.name; +// await page.goto(scenario.url); +// console.log(`Checking: ${currentScenario}: ${scenario.description}`); +// await page.close(); +// } + +// function onConsoleErr(msg) { +// if (msg.type === 'error') { +// console.error(`ERROR Found in ${currentScenario}`); +// browser.close(); +// process.exit(1); +// } +// } + +// // Returns a random value between 1 and the provided length. +// // Note: indexing of keys starts at 1, not 0 +// function getRandomKey(menuItemsLength) { +// return Math.floor(Math.random() * (menuItemsLength - 1) + 1); +// } diff --git a/tsconfig.json b/tsconfig.json index c8ae687b..f52964a7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,7 +7,7 @@ "emitDecoratorMetadata": true, "experimentalDecorators": true, "lib": ["es6", "dom"], - "module": "es6", + "module": "commonjs", "moduleResolution": "node", "noImplicitAny": true, "target": "es5", diff --git a/tslint.json b/tslint.json index 078efc1f..e061baf2 100644 --- a/tslint.json +++ b/tslint.json @@ -8,7 +8,7 @@ true, "check-space" ], - "curly": true, + "curly": false, "eofline": true, "forin": true, "indent": [ From 2a12b82c21f7e9ed0401cc83136ca85d20505db6 Mon Sep 17 00:00:00 2001 From: mgmarlow Date: Mon, 27 Nov 2017 14:01:46 -0800 Subject: [PATCH 02/19] successfully run sandboxes --- src/cli/shared/parser.ts | 4 +- src/cli/verify-sandboxes/verify-sandboxes.ts | 210 +++++++++---------- 2 files changed, 101 insertions(+), 113 deletions(-) diff --git a/src/cli/shared/parser.ts b/src/cli/shared/parser.ts index 77115b8c..6064219e 100644 --- a/src/cli/shared/parser.ts +++ b/src/cli/shared/parser.ts @@ -8,7 +8,7 @@ export function getParsedArguments(supportedFlags: string[], args: string[]) const flags: string[] = []; args = args.reduce((accr, value) => { - const flag = getArgumentValue(value); + const flag = getFlag(value); supportedFlags.indexOf(flag) > -1 ? flags.push(value) : accr.push(value); return accr; }, []); @@ -21,6 +21,6 @@ export function getParsedArguments(supportedFlags: string[], args: string[]) * e.g. --path=./src/ * @param argument - Flag argument */ -export function getArgumentValue(argument: string): string { +function getFlag(argument: string): string { return argument.split('=')[0]; } diff --git a/src/cli/verify-sandboxes/verify-sandboxes.ts b/src/cli/verify-sandboxes/verify-sandboxes.ts index 9548ffc1..0da1f2d2 100644 --- a/src/cli/verify-sandboxes/verify-sandboxes.ts +++ b/src/cli/verify-sandboxes/verify-sandboxes.ts @@ -1,13 +1,26 @@ import * as puppeteer from 'puppeteer'; import * as process from 'process'; -import { getParsedArguments, getArgumentValue } from '../shared/parser'; +import * as path from 'path'; +import { getParsedArguments } from '../shared/parser'; -// TODO: Update Docs! +// TODO: Update usage documentation +// TODO: Doc comments + +interface ScenarioSummary { + url: string; + name: string; + description: string; +} class Configuration { + public chromeArguments = [ + '--disable-gpu', + '--no-sandbox' + ]; + constructor ( - public path: string, - public build: boolean, + public sandboxPath: string, + public buildMode: boolean, public port: number, ) {} @@ -21,11 +34,6 @@ const supportedFlages = ['--path', '--build', '--port']; const parameters = process.argv; const parsedArguments = getParsedArguments(supportedFlages, parameters); -const chromeArguments = [ - '--disable-gpu', - '--no-sandbox' -]; - let browser: any; let currentScenario = ''; @@ -34,53 +42,14 @@ process.on('unhandledRejection', () => { if (browser) browser.close(); }); - -// const cmd_args = process.argv.filter(arg => arg.includes('--')); -// const sandboxesPath = cmd_args[0].substring('--path='.length, cmd_args[0].length); -// const selectRandomScenario = cmd_args[1] === '--build'; - - -// const BASE_URL = 'http://localhost:8002/'; -// let currentScenario = ''; -// let browser; - -// // Ensure Chromium instances are destroyed -// process.on('unhandledRejection', () => { -// if (browser) { -// browser.close(); -// } -// }); - (async () => { const configuration = configure(parsedArguments.flags); - // await main(); + await main(configuration); })(); ///////////////////////////////// -// interface ScenarioSummary { -// url: string; -// name: string; -// description: string; -// } - -// async function main () { -// browser = await puppeteer.launch({ -// headless: true, -// handleSIGINT: false, -// args -// }); - -// const scenarios = getSandboxMetadata(selectRandomScenario); -// console.log(`Retrieved ${scenarios.length} scenarios.\n`); -// for (let i = 0; i < scenarios.length; i++) { -// await work(scenarios[i]); -// } - -// browser.close(); -// } - function configure(flags: string[]): Configuration { const pathArg = flags.find(f => f.includes('--path')); const portArg = flags.find(f => f.includes('--port')); @@ -90,70 +59,89 @@ function configure(flags: string[]): Configuration { process.exit(1); } - const path = getArgumentValue(pathArg); + const sandboxLocation = path.join(process.cwd(), getArgumentValue(pathArg)); const buildMode = flags.indexOf('--build') !== -1; const port = portArg ? parseInt(getArgumentValue(portArg), 10) : 8002; - return new Configuration(path, buildMode, port); + return new Configuration(sandboxLocation, buildMode, port); +} + +async function main (configuration: Configuration) { + browser = await puppeteer.launch({ + headless: true, + handleSIGINT: false, + args: configuration.chromeArguments + }); + + const scenarios = getSandboxMetadata(configuration.baseUrl, configuration.buildMode, configuration.sandboxPath); + console.log(`Retrieved ${scenarios.length} scenarios.\n`); + for (let i = 0; i < scenarios.length; i++) { + await work(scenarios[i]); + } + + browser.close(); +} + +function getSandboxMetadata(baseUrl: string, selectRandomScenario: boolean, path: string): ScenarioSummary[] { + const scenarios: ScenarioSummary[] = []; + + loadSandboxMenuItems(path).forEach((scenario: any) => { + if (selectRandomScenario) { + const randomItemKey = getRandomKey(scenario.scenarioMenuItems.length); + scenario.scenarioMenuItems + .forEach((item: any) => { + if (item.key === randomItemKey) { + const url = `${baseUrl}?scenario=${encodeURIComponent(scenario.key)}/${encodeURIComponent(item.description)}`; + scenarios.push({ url, name: scenario.key, description: item.description }); + } + }); + } else { + // Grab all scenarios + scenario.scenarioMenuItems + .forEach((item: any) => { + const url = `${baseUrl}?scenario=${encodeURIComponent(scenario.key)}/${encodeURIComponent(item.description)}`; + scenarios.push({ url, name: scenario.key, description: item.description }); + }); + } + }); + + return scenarios; +} + +function loadSandboxMenuItems(path: string) { + try { + return require(path).getSandboxMenuItems(); + } catch (err) { + console.error('Failed to load sandboxes.ts file.'); + console.error(err); + console.log('Terminating process.'); + process.exit(1); + } +} + +async function work(scenario: ScenarioSummary) { + const page = await browser.newPage(); + page.on('console', (msg: any) => onConsoleErr(msg)); + currentScenario = scenario.name; + await page.goto(scenario.url); + console.log(`Checking: ${currentScenario}: ${scenario.description}`); + await page.close(); +} + +function onConsoleErr(msg: any) { + if (msg.type === 'error') { + console.error(`ERROR Found in ${currentScenario}`); + browser.close(); + process.exit(1); + } +} + +// Returns a random value between 1 and the provided length. +// Note: indexing of keys starts at 1, not 0 +function getRandomKey(menuItemsLength: number): number { + return Math.floor(Math.random() * (menuItemsLength - 1) + 1); } -// function getSandboxMetadata(randomScenario) { -// const scenarios: ScenarioSummary[] = []; - -// loadSandboxMenuItems(sandboxesPath).forEach(scenario => { -// if (randomScenario) { -// // Pick a random scenario for each component -// const randomItemKey = getRandomKey(scenario.scenarioMenuItems.length); -// scenario.scenarioMenuItems -// .forEach(item => { -// if (item.key === randomItemKey) { -// const url = `${BASE_URL}?scenario=${encodeURIComponent(scenario.key)}/${encodeURIComponent(item.description)}`; -// scenarios.push({ url, name: scenario.key, description: item.description }); -// } -// }); -// } else { -// // Grab all scenarios -// scenario.scenarioMenuItems -// .forEach(item => { -// const url = `${BASE_URL}?scenario=${encodeURIComponent(scenario.key)}/${encodeURIComponent(item.description)}`; -// scenarios.push({ url, name: scenario.key, description: item.description }); -// }); -// } -// }); - -// return scenarios; -// } - -// function loadSandboxMenuItems(path: string) { -// try { -// return require(path).getSandboxMenuItems(); -// } catch (err) { -// console.error('Failed to load sandboxes.ts file.'); -// console.error(err); -// console.log('Terminating process.'); -// process.exit(1); -// } -// } - -// async function work (scenario) { -// const page = await browser.newPage(); -// page.on('console', msg => onConsoleErr(msg)); -// currentScenario = scenario.name; -// await page.goto(scenario.url); -// console.log(`Checking: ${currentScenario}: ${scenario.description}`); -// await page.close(); -// } - -// function onConsoleErr(msg) { -// if (msg.type === 'error') { -// console.error(`ERROR Found in ${currentScenario}`); -// browser.close(); -// process.exit(1); -// } -// } - -// // Returns a random value between 1 and the provided length. -// // Note: indexing of keys starts at 1, not 0 -// function getRandomKey(menuItemsLength) { -// return Math.floor(Math.random() * (menuItemsLength - 1) + 1); -// } +function getArgumentValue(argument: string) { + return argument.split('=')[1]; +} From f58ddc9b2e5bb28c3f219a644f45b40e5ffe61b5 Mon Sep 17 00:00:00 2001 From: mgmarlow Date: Mon, 27 Nov 2017 14:02:57 -0800 Subject: [PATCH 03/19] default path is now 4201 --- src/cli/verify-sandboxes/verify-sandboxes.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cli/verify-sandboxes/verify-sandboxes.ts b/src/cli/verify-sandboxes/verify-sandboxes.ts index 0da1f2d2..5cfd13a4 100644 --- a/src/cli/verify-sandboxes/verify-sandboxes.ts +++ b/src/cli/verify-sandboxes/verify-sandboxes.ts @@ -61,7 +61,7 @@ function configure(flags: string[]): Configuration { const sandboxLocation = path.join(process.cwd(), getArgumentValue(pathArg)); const buildMode = flags.indexOf('--build') !== -1; - const port = portArg ? parseInt(getArgumentValue(portArg), 10) : 8002; + const port = portArg ? parseInt(getArgumentValue(portArg), 10) : 4201; return new Configuration(sandboxLocation, buildMode, port); } From dbfec5379ee47cb008a552c8ed88c4e559348258 Mon Sep 17 00:00:00 2001 From: mgmarlow Date: Mon, 27 Nov 2017 14:29:52 -0800 Subject: [PATCH 04/19] Add error reporter types and documentation --- package.json | 2 +- src/cli/verify-sandboxes/state.ts | 51 ++++++++++++ src/cli/verify-sandboxes/verify-sandboxes.ts | 84 +++++++++++--------- 3 files changed, 99 insertions(+), 38 deletions(-) create mode 100644 src/cli/verify-sandboxes/state.ts diff --git a/package.json b/package.json index 175891af..99c7a5a4 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "app:build": "gulp build", "cli:build": "tsc -p ./src/cli/tsconfig.json", "prepublish": "npm run build", - "test": "ts-node ./src/cli/verify-sandboxes/verify-sandboxes.ts --path=./examples/example-app-angular-cli/src/sandboxes.ts --build" + "test": "ts-node ./src/cli/verify-sandboxes/verify-sandboxes.ts --path=./examples/example-app-angular-cli/src/sandboxes.ts" }, "repository": { "type": "git", diff --git a/src/cli/verify-sandboxes/state.ts b/src/cli/verify-sandboxes/state.ts new file mode 100644 index 00000000..bce5fc3e --- /dev/null +++ b/src/cli/verify-sandboxes/state.ts @@ -0,0 +1,51 @@ +export interface ScenarioSummary { + url: string; + name: string; + description: string; +} + +export class Configuration { + public chromeArguments = [ + '--disable-gpu', + '--no-sandbox' + ]; + + constructor ( + public sandboxPath: string, + public buildMode: boolean, + public port: number, + ) {} + + get baseUrl(): string { + return `http://localhost:${this.port}`; + } +} + +export enum ReportType { + Log +} + +export class ErrorReporter { + private _errors: { error: any, scenario: string }[] = []; + + constructor(public type = ReportType.Log) {} + + get errors() { + return this._errors; + } + + + + addError(error: any, scenario: string) { + this._errors.push({ error, scenario }); + } + + compileReport() { + switch (this.type) { + case ReportType.Log: + console.log('Found errors in the following scenarios:'); + this._errors.forEach(e => console.log(e.scenario)); + } + } + +} diff --git a/src/cli/verify-sandboxes/verify-sandboxes.ts b/src/cli/verify-sandboxes/verify-sandboxes.ts index 5cfd13a4..f88040be 100644 --- a/src/cli/verify-sandboxes/verify-sandboxes.ts +++ b/src/cli/verify-sandboxes/verify-sandboxes.ts @@ -2,33 +2,11 @@ import * as puppeteer from 'puppeteer'; import * as process from 'process'; import * as path from 'path'; import { getParsedArguments } from '../shared/parser'; +import { Configuration, ScenarioSummary, ErrorReporter } from './state'; // TODO: Update usage documentation // TODO: Doc comments -interface ScenarioSummary { - url: string; - name: string; - description: string; -} - -class Configuration { - public chromeArguments = [ - '--disable-gpu', - '--no-sandbox' - ]; - - constructor ( - public sandboxPath: string, - public buildMode: boolean, - public port: number, - ) {} - - get baseUrl(): string { - return `http://localhost:${this.port}`; - } -} - // Parse command line input const supportedFlages = ['--path', '--build', '--port']; const parameters = process.argv; @@ -36,6 +14,7 @@ const parsedArguments = getParsedArguments(supportedFlages, parameters); let browser: any; let currentScenario = ''; +const reporter = new ErrorReporter(); // Ensure Chromium instances are destroyed on err process.on('unhandledRejection', () => { @@ -80,8 +59,34 @@ async function main (configuration: Configuration) { } browser.close(); + + if (reporter.errors.length > 0) { + reporter.compileReport(); + process.exit(1); + } else { + process.exit(0); + } } +/** + * Creates a Chromium page and navigates to a scenario + * @param scenario - Scenario to visit + */ +async function work(scenario: ScenarioSummary) { + const page = await browser.newPage(); + page.on('console', (msg: any) => onConsoleErr(msg)); + currentScenario = scenario.name; + await page.goto(scenario.url); + console.log(`Checking: ${currentScenario}: ${scenario.description}`); + await page.close(); +} + +/** + * Retrieves Sandbox scenario URLs, descriptions, and names + * @param baseUrl - Base URL of scenario path e.g. http://localhost:4201 + * @param selectRandomScenario - Whether or not to select one random scenario of all availalble scenarios for a component + * @param path - Path to sandboxes.ts + */ function getSandboxMetadata(baseUrl: string, selectRandomScenario: boolean, path: string): ScenarioSummary[] { const scenarios: ScenarioSummary[] = []; @@ -108,7 +113,11 @@ function getSandboxMetadata(baseUrl: string, selectRandomScenario: boolean, path return scenarios; } -function loadSandboxMenuItems(path: string) { +/** + * Attemtp to load sandboxes.ts and provide menu items + * @param path - Path to sandboxes.ts + */ +function loadSandboxMenuItems(path: string): any[] { try { return require(path).getSandboxMenuItems(); } catch (err) { @@ -119,29 +128,30 @@ function loadSandboxMenuItems(path: string) { } } -async function work(scenario: ScenarioSummary) { - const page = await browser.newPage(); - page.on('console', (msg: any) => onConsoleErr(msg)); - currentScenario = scenario.name; - await page.goto(scenario.url); - console.log(`Checking: ${currentScenario}: ${scenario.description}`); - await page.close(); -} - +/** + * Callback when Chromium page encounters a console error + * @param msg - Error message + */ function onConsoleErr(msg: any) { if (msg.type === 'error') { console.error(`ERROR Found in ${currentScenario}`); - browser.close(); - process.exit(1); + reporter.addError(msg, currentScenario); } } -// Returns a random value between 1 and the provided length. -// Note: indexing of keys starts at 1, not 0 +/** + * Returns a random value between 1 and the provided length. + * Note: indexing of keys starts at 1, not 0 + * @param menuItemsLength - Maximum number of items + */ function getRandomKey(menuItemsLength: number): number { return Math.floor(Math.random() * (menuItemsLength - 1) + 1); } +/** + * Separates value of an argument from its flag + * @param argument - Flag with value e.g. --path=./src/ + */ function getArgumentValue(argument: string) { return argument.split('=')[1]; } From ce8a68e6365d1fc8c8285354df69a64caa966c38 Mon Sep 17 00:00:00 2001 From: mgmarlow Date: Mon, 27 Nov 2017 15:59:02 -0800 Subject: [PATCH 05/19] poll scenario repeatedly to prevent early disconnects --- src/cli/verify-sandboxes/state.ts | 1 + src/cli/verify-sandboxes/verify-sandboxes.ts | 24 ++++++++++++++------ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/cli/verify-sandboxes/state.ts b/src/cli/verify-sandboxes/state.ts index bce5fc3e..5f26b685 100644 --- a/src/cli/verify-sandboxes/state.ts +++ b/src/cli/verify-sandboxes/state.ts @@ -14,6 +14,7 @@ export class Configuration { public sandboxPath: string, public buildMode: boolean, public port: number, + public timeoutAttempts = 20 ) {} get baseUrl(): string { diff --git a/src/cli/verify-sandboxes/verify-sandboxes.ts b/src/cli/verify-sandboxes/verify-sandboxes.ts index f88040be..1d27129c 100644 --- a/src/cli/verify-sandboxes/verify-sandboxes.ts +++ b/src/cli/verify-sandboxes/verify-sandboxes.ts @@ -4,9 +4,6 @@ import * as path from 'path'; import { getParsedArguments } from '../shared/parser'; import { Configuration, ScenarioSummary, ErrorReporter } from './state'; -// TODO: Update usage documentation -// TODO: Doc comments - // Parse command line input const supportedFlages = ['--path', '--build', '--port']; const parameters = process.argv; @@ -46,6 +43,7 @@ function configure(flags: string[]): Configuration { } async function main (configuration: Configuration) { + let timeoutAttempts = configuration.timeoutAttempts; browser = await puppeteer.launch({ headless: true, handleSIGINT: false, @@ -55,7 +53,7 @@ async function main (configuration: Configuration) { const scenarios = getSandboxMetadata(configuration.baseUrl, configuration.buildMode, configuration.sandboxPath); console.log(`Retrieved ${scenarios.length} scenarios.\n`); for (let i = 0; i < scenarios.length; i++) { - await work(scenarios[i]); + await openScenarioInNewPage(scenarios[i], configuration.timeoutAttempts); } browser.close(); @@ -69,14 +67,26 @@ async function main (configuration: Configuration) { } /** - * Creates a Chromium page and navigates to a scenario + * Creates a Chromium page and navigates to a scenario (URL) * @param scenario - Scenario to visit */ -async function work(scenario: ScenarioSummary) { +async function openScenarioInNewPage(scenario: ScenarioSummary, timeoutAttempts: number) { + if (timeoutAttempts === 0) { + await browser.close(); + process.exit(1); + } + const page = await browser.newPage(); page.on('console', (msg: any) => onConsoleErr(msg)); currentScenario = scenario.name; - await page.goto(scenario.url); + + try { + await page.goto(scenario.url); await page.goto(scenario.url); + } catch (e) { + console.log(`Failed to connect. Attempting to Reconnect. (Attempts Remaining ${timeoutAttempts})`); + await openScenarioInNewPage(scenario, timeoutAttempts - 1); + } + console.log(`Checking: ${currentScenario}: ${scenario.description}`); await page.close(); } From db0dff210740d250c59081d418dc3844840c1cad Mon Sep 17 00:00:00 2001 From: mgmarlow Date: Mon, 27 Nov 2017 15:59:41 -0800 Subject: [PATCH 06/19] improve connection message --- src/cli/verify-sandboxes/verify-sandboxes.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cli/verify-sandboxes/verify-sandboxes.ts b/src/cli/verify-sandboxes/verify-sandboxes.ts index 1d27129c..f1bb5096 100644 --- a/src/cli/verify-sandboxes/verify-sandboxes.ts +++ b/src/cli/verify-sandboxes/verify-sandboxes.ts @@ -83,7 +83,7 @@ async function openScenarioInNewPage(scenario: ScenarioSummary, timeoutAttempts: try { await page.goto(scenario.url); await page.goto(scenario.url); } catch (e) { - console.log(`Failed to connect. Attempting to Reconnect. (Attempts Remaining ${timeoutAttempts})`); + console.log(`Attempting to connect. (Attempts Remaining ${timeoutAttempts})`); await openScenarioInNewPage(scenario, timeoutAttempts - 1); } From 35f3666a7ac917702a62937b12ffe437c0fb9241 Mon Sep 17 00:00:00 2001 From: mgmarlow Date: Mon, 27 Nov 2017 15:59:56 -0800 Subject: [PATCH 07/19] fix typo --- src/cli/verify-sandboxes/verify-sandboxes.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cli/verify-sandboxes/verify-sandboxes.ts b/src/cli/verify-sandboxes/verify-sandboxes.ts index f1bb5096..d4cb24f0 100644 --- a/src/cli/verify-sandboxes/verify-sandboxes.ts +++ b/src/cli/verify-sandboxes/verify-sandboxes.ts @@ -81,7 +81,7 @@ async function openScenarioInNewPage(scenario: ScenarioSummary, timeoutAttempts: currentScenario = scenario.name; try { - await page.goto(scenario.url); await page.goto(scenario.url); + await page.goto(scenario.url); } catch (e) { console.log(`Attempting to connect. (Attempts Remaining ${timeoutAttempts})`); await openScenarioInNewPage(scenario, timeoutAttempts - 1); From 35923509e78e6977b3b16a2d9c8c3fadc92b6b55 Mon Sep 17 00:00:00 2001 From: mgmarlow Date: Mon, 27 Nov 2017 16:45:47 -0800 Subject: [PATCH 08/19] Modify playground CLI to use async/await for verify sandboxes extension --- examples/example-app-angular-cli/package.json | 1 + src/cli/build.ts | 17 ++++++--- src/cli/cli.ts | 38 ++----------------- src/cli/run.ts | 37 ++++++++++++++++++ src/cli/verify-sandboxes/verify-sandboxes.ts | 10 ++++- tsconfig.json | 2 +- 6 files changed, 62 insertions(+), 43 deletions(-) create mode 100644 src/cli/run.ts diff --git a/examples/example-app-angular-cli/package.json b/examples/example-app-angular-cli/package.json index b767c487..0c94882c 100644 --- a/examples/example-app-angular-cli/package.json +++ b/examples/example-app-angular-cli/package.json @@ -7,6 +7,7 @@ "ng": "ng", "playground:copy": "node ../../scripts/copy.js", "playground:run": "node ./node_modules/angular-playground/dist/bin/cli.js", + "playground:test": "node ./node_modules/angular-playground/dist/bin/verify-sandboxes/verify-sandboxes.js --path=./src/sandboxes.ts", "playground": "npm run playground:copy && npm run playground:run", "playground:dev": "npm run build --prefix ../../ && npm run playground", "playground2:run": "node ./node_modules/angular-playground/dist/bin/cli.js angular-playground2.json", diff --git a/src/cli/build.ts b/src/cli/build.ts index cab8ee7c..ddc1aee3 100644 --- a/src/cli/build.ts +++ b/src/cli/build.ts @@ -3,7 +3,7 @@ import { StringBuilder } from './string-builder'; import * as fs from 'fs'; import * as path from 'path'; -export const build = (rootPath) => { +export const build = (rootPath): Promise => { let content = new StringBuilder(); let home = path.resolve(rootPath); let sandboxes = []; @@ -50,10 +50,15 @@ export const build = (rootPath) => { content.addLine(`}}`); let filePath = path.resolve(home, './sandboxes.ts'); - fs.writeFile(filePath, content.dump(), function (err) { - if (err) { - return console.log(err); - } - console.log(`Created file: ${filePath}`); + + return new Promise((resolve, reject) => { + fs.writeFile(filePath, content.dump(), function (err) { + if (err) { + reject(err); + } else { + console.log(`Created file: ${filePath}`); + resolve(content); + } + }); }); }; diff --git a/src/cli/cli.ts b/src/cli/cli.ts index adbd41ed..1b1193ea 100644 --- a/src/cli/cli.ts +++ b/src/cli/cli.ts @@ -1,35 +1,5 @@ #! /usr/bin/env node -const path = require('path'); -import {build} from './build'; -import {startWatch} from './start-watch'; -import {runAngularCli} from './run-angular-cli'; - -const supportedFlags = ['-no-watch', '-no-serve']; -let args = process.argv.slice(2); -let flags = []; -args = args.reduce((accr, value) => { - supportedFlags.indexOf(value) > -1 ? flags.push(value) : accr.push(value); - return accr; -}, []); - -const runWatch = flags.indexOf('-no-watch') === -1; -const runAngularCliServe = flags.indexOf('-no-serve') === -1; - -const configFilePath = args[0] || 'angular-playground.json'; - -let configFile = path.resolve(configFilePath); -let config; -try { - config = require(configFile.replace(/.json$/, '')); -} catch(e) { - process.stdout.write(`[angular-playground]: \x1b[31mFailed to load config file ${configFile}\x1b[0m\n`); - process.exit(1); -} - -build(config.sourceRoot); -if (runWatch) { - startWatch(config, () => build(config.sourceRoot)); -} -if (runAngularCliServe && config.angularCli) { - runAngularCli(config.angularCli); -} +import { runPlayground } from './run'; +(async () => { + await runPlayground(); +})(); diff --git a/src/cli/run.ts b/src/cli/run.ts new file mode 100644 index 00000000..a8f7bf2c --- /dev/null +++ b/src/cli/run.ts @@ -0,0 +1,37 @@ +const path = require('path'); +import {build} from './build'; +import {startWatch} from './start-watch'; +import {runAngularCli} from './run-angular-cli'; + +export async function runPlayground() { + const supportedFlags = ['-no-watch', '-no-serve']; + let args = process.argv.slice(2); + let flags = []; + args = args.reduce((accr, value) => { + supportedFlags.indexOf(value) > -1 ? flags.push(value) : accr.push(value); + return accr; + }, []); + + const runWatch = flags.indexOf('-no-watch') === -1; + const runAngularCliServe = flags.indexOf('-no-serve') === -1; + + // TODO: Support multiple arguments (for verification) + const configFilePath = args[0] || 'angular-playground.json'; + + let configFile = path.resolve(configFilePath); + let config; + try { + config = require(configFile.replace(/.json$/, '')); + } catch(e) { + process.stdout.write(`[angular-playground]: \x1b[31mFailed to load config file ${configFile}\x1b[0m\n`); + process.exit(1); + } + + await build(config.sourceRoot); + if (runWatch) { + startWatch(config, () => build(config.sourceRoot)); + } + if (runAngularCliServe && config.angularCli) { + runAngularCli(config.angularCli); + } +} diff --git a/src/cli/verify-sandboxes/verify-sandboxes.ts b/src/cli/verify-sandboxes/verify-sandboxes.ts index d4cb24f0..bc240769 100644 --- a/src/cli/verify-sandboxes/verify-sandboxes.ts +++ b/src/cli/verify-sandboxes/verify-sandboxes.ts @@ -1,8 +1,10 @@ +#! /usr/bin/env node import * as puppeteer from 'puppeteer'; import * as process from 'process'; import * as path from 'path'; import { getParsedArguments } from '../shared/parser'; import { Configuration, ScenarioSummary, ErrorReporter } from './state'; +import { runPlayground } from '../run'; // Parse command line input const supportedFlages = ['--path', '--build', '--port']; @@ -18,7 +20,9 @@ process.on('unhandledRejection', () => { if (browser) browser.close(); }); +// Begin browser tasks (async () => { + await runPlayground(); const configuration = configure(parsedArguments.flags); await main(configuration); })(); @@ -67,7 +71,9 @@ async function main (configuration: Configuration) { } /** - * Creates a Chromium page and navigates to a scenario (URL) + * Creates a Chromium page and navigates to a scenario (URL). + * If Chromium is not able to connect to the provided page, it will issue a series + * of retries before it finally fails. * @param scenario - Scenario to visit */ async function openScenarioInNewPage(scenario: ScenarioSummary, timeoutAttempts: number) { @@ -83,7 +89,7 @@ async function openScenarioInNewPage(scenario: ScenarioSummary, timeoutAttempts: try { await page.goto(scenario.url); } catch (e) { - console.log(`Attempting to connect. (Attempts Remaining ${timeoutAttempts})`); + console.log(`Attempting to connect. (Attempts Remaining: ${timeoutAttempts})`); await openScenarioInNewPage(scenario, timeoutAttempts - 1); } diff --git a/tsconfig.json b/tsconfig.json index f52964a7..be326341 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -9,7 +9,7 @@ "lib": ["es6", "dom"], "module": "commonjs", "moduleResolution": "node", - "noImplicitAny": true, + "noImplicitAny": false, "target": "es5", "newLine": "LF", "typeRoots": [ From b1caa9fa17cf5af320b2603cd21ccc2f8c1e7cec Mon Sep 17 00:00:00 2001 From: mgmarlow Date: Mon, 27 Nov 2017 16:53:57 -0800 Subject: [PATCH 09/19] remove unnecessary ts-node dep --- package.json | 4 +--- tsconfig.json | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 99c7a5a4..d1a3fd2c 100644 --- a/package.json +++ b/package.json @@ -16,8 +16,7 @@ "build": "npm run app:build && npm run cli:build", "app:build": "gulp build", "cli:build": "tsc -p ./src/cli/tsconfig.json", - "prepublish": "npm run build", - "test": "ts-node ./src/cli/verify-sandboxes/verify-sandboxes.ts --path=./examples/example-app-angular-cli/src/sandboxes.ts" + "prepublish": "npm run build" }, "repository": { "type": "git", @@ -66,7 +65,6 @@ "gulp-inline-ng2-template": "^4.0.0", "puppeteer": "^0.13.0", "rxjs": "^5.5.2", - "ts-node": "^3.3.0", "tslint": "5.3.2", "typescript": "2.4.2", "zone.js": "0.8.4" diff --git a/tsconfig.json b/tsconfig.json index be326341..a8ce0ebb 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,7 +7,7 @@ "emitDecoratorMetadata": true, "experimentalDecorators": true, "lib": ["es6", "dom"], - "module": "commonjs", + "module": "es2015", "moduleResolution": "node", "noImplicitAny": false, "target": "es5", From 76b4b535ee61a7513bb36532278c56ef694eda97 Mon Sep 17 00:00:00 2001 From: mgmarlow Date: Mon, 27 Nov 2017 16:59:59 -0800 Subject: [PATCH 10/19] change module to es6 --- tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tsconfig.json b/tsconfig.json index a8ce0ebb..64f15ffe 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,7 +7,7 @@ "emitDecoratorMetadata": true, "experimentalDecorators": true, "lib": ["es6", "dom"], - "module": "es2015", + "module": "es6", "moduleResolution": "node", "noImplicitAny": false, "target": "es5", From 8069fdb5ffa37023a4fe9c93fc046e515d6e2ddf Mon Sep 17 00:00:00 2001 From: mgmarlow Date: Tue, 28 Nov 2017 11:40:01 -0800 Subject: [PATCH 11/19] extend playground CLI arguments and features --- src/cli/cli.ts | 34 +++++++- src/cli/configuration.ts | 82 ++++++++++++++++++++ src/cli/run.ts | 37 --------- src/cli/verify-sandboxes/verify-sandboxes.ts | 16 ++-- 4 files changed, 124 insertions(+), 45 deletions(-) create mode 100644 src/cli/configuration.ts delete mode 100644 src/cli/run.ts diff --git a/src/cli/cli.ts b/src/cli/cli.ts index 1b1193ea..9febcf01 100644 --- a/src/cli/cli.ts +++ b/src/cli/cli.ts @@ -1,5 +1,35 @@ #! /usr/bin/env node -import { runPlayground } from './run'; +import * as path from 'path'; +import { build } from './build'; +import { startWatch } from './start-watch'; +import { runAngularCli } from './run-angular-cli'; +import { getParsedArguments } from 'shared/parser'; +import { Configuration } from './configuration'; + (async () => { - await runPlayground(); + await run(); })(); + +async function run() { + const rawArgs = process.argv.slice(2); + const config = new Configuration(rawArgs); + + let configFile = path.resolve(config.configFilePath); + let playgroundConfig; + try { + playgroundConfig = require(configFile.replace(/.json$/, '')); + } catch (e) { + process.stdout.write(`[angular-playground]: \x1b[31mFailed to load config file ${configFile}\x1b[0m\n`); + process.exit(1); + } + + await build(playgroundConfig.sourceRoot); + + if (config.runWatch) { + startWatch(playgroundConfig, () => build(playgroundConfig.sourceRoot)); + } + + if (config.runAngularCliServe && playgroundConfig.angularCli) { + runAngularCli(playgroundConfig.angularCli); + } +} diff --git a/src/cli/configuration.ts b/src/cli/configuration.ts new file mode 100644 index 00000000..608b9021 --- /dev/null +++ b/src/cli/configuration.ts @@ -0,0 +1,82 @@ +/** + * Configuration object used to parse and assign command line arguments + */ +export class Configuration { + private supportedFlags = { + noWatch: '--no-watch', + noServe: '--no-serve', + checkErrs: '--check-errors', + randomScenario: '--random-scenario' + }; + + private supportedArguments = { + config: '--config' + }; + + runWatch: boolean; + runAngularCliServe: boolean; + runCheckErrors: boolean; + randomScenario: boolean; + configFilePath: string; + port = 4201; + timeoutAttempts = 20; + chromeArguments = [ '--disable-gpu', '--no-sandbox' ]; + + constructor(rawArgv: string[]) { + const { flags, args } = this.getParsedArguments(rawArgv); + this.configureFlags(flags); + this.configureArguments(args); + } + + get baseUrl(): string { + return `http://localhost:${this.port}`; + } + + // Boolean flags + private configureFlags(flags: string[]) { + this.runWatch = flags.indexOf(this.supportedFlags.noWatch) === -1; + this.runAngularCliServe = flags.indexOf(this.supportedFlags.noServe) === -1; + this.runCheckErrors = flags.indexOf(this.supportedFlags.checkErrs) !== -1; + this.randomScenario = flags.indexOf(this.supportedFlags.randomScenario) !== -1; + } + + // Arguments that may have values attached + private configureArguments(args: string[]) { + const configIndex = args.indexOf(this.supportedArguments.config); + if (configIndex !== -1) { + this.configFilePath = this.getArgValue(configIndex, args); + } else if (args.length > 0) { + this.configFilePath = args[0]; + } else { + this.configFilePath = 'angular-playground.json'; + } + } + + /** + * Separates accepted command line arguments from other ts-node arguments + * @param supportedFlags - Accepted command line flags + * @param args - Process arguments + */ + private getParsedArguments(args: string[]): { flags: string[], args: string[] } { + const flags: string[] = []; + + args = args.reduce((accr, value) => { + Object.keys(this.supportedFlags) + .map(key => this.supportedFlags[key]) + .indexOf(value) > -1 ? flags.push(value) : accr.push(value); + return accr; + }, []); + + return { flags, args }; + } + + /** + * Gets the value of an argument from list of args (next consecutive argument) + * e.g. --config ./src/ + * @param startingIndex - Index of argument + * @param args - list of args + */ + private getArgValue(startingIndex: number, args: string[]): string { + return args[startingIndex + 1]; + } +} diff --git a/src/cli/run.ts b/src/cli/run.ts deleted file mode 100644 index a8f7bf2c..00000000 --- a/src/cli/run.ts +++ /dev/null @@ -1,37 +0,0 @@ -const path = require('path'); -import {build} from './build'; -import {startWatch} from './start-watch'; -import {runAngularCli} from './run-angular-cli'; - -export async function runPlayground() { - const supportedFlags = ['-no-watch', '-no-serve']; - let args = process.argv.slice(2); - let flags = []; - args = args.reduce((accr, value) => { - supportedFlags.indexOf(value) > -1 ? flags.push(value) : accr.push(value); - return accr; - }, []); - - const runWatch = flags.indexOf('-no-watch') === -1; - const runAngularCliServe = flags.indexOf('-no-serve') === -1; - - // TODO: Support multiple arguments (for verification) - const configFilePath = args[0] || 'angular-playground.json'; - - let configFile = path.resolve(configFilePath); - let config; - try { - config = require(configFile.replace(/.json$/, '')); - } catch(e) { - process.stdout.write(`[angular-playground]: \x1b[31mFailed to load config file ${configFile}\x1b[0m\n`); - process.exit(1); - } - - await build(config.sourceRoot); - if (runWatch) { - startWatch(config, () => build(config.sourceRoot)); - } - if (runAngularCliServe && config.angularCli) { - runAngularCli(config.angularCli); - } -} diff --git a/src/cli/verify-sandboxes/verify-sandboxes.ts b/src/cli/verify-sandboxes/verify-sandboxes.ts index bc240769..9b1c204a 100644 --- a/src/cli/verify-sandboxes/verify-sandboxes.ts +++ b/src/cli/verify-sandboxes/verify-sandboxes.ts @@ -4,16 +4,16 @@ import * as process from 'process'; import * as path from 'path'; import { getParsedArguments } from '../shared/parser'; import { Configuration, ScenarioSummary, ErrorReporter } from './state'; -import { runPlayground } from '../run'; // Parse command line input const supportedFlages = ['--path', '--build', '--port']; -const parameters = process.argv; +const parameters = process.argv.slice(2); const parsedArguments = getParsedArguments(supportedFlages, parameters); let browser: any; let currentScenario = ''; const reporter = new ErrorReporter(); +const SANDBOXES_PATH = './src/sandboxes.ts'; // Ensure Chromium instances are destroyed on err process.on('unhandledRejection', () => { @@ -21,11 +21,15 @@ process.on('unhandledRejection', () => { }); // Begin browser tasks -(async () => { - await runPlayground(); +// (async () => { +// await runPlayground(); +// await verifySandboxes(); +// })(); + +export async function verifySandboxes() { const configuration = configure(parsedArguments.flags); await main(configuration); -})(); +} ///////////////////////////////// @@ -54,7 +58,7 @@ async function main (configuration: Configuration) { args: configuration.chromeArguments }); - const scenarios = getSandboxMetadata(configuration.baseUrl, configuration.buildMode, configuration.sandboxPath); + const scenarios = getSandboxMetadata(configuration.baseUrl, configuration.buildMode, SANDBOXES_PATH); console.log(`Retrieved ${scenarios.length} scenarios.\n`); for (let i = 0; i < scenarios.length; i++) { await openScenarioInNewPage(scenarios[i], configuration.timeoutAttempts); From 607458672156a8f2b20ac8f13f5ae797c70c3666 Mon Sep 17 00:00:00 2001 From: mgmarlow Date: Tue, 28 Nov 2017 12:23:48 -0800 Subject: [PATCH 12/19] restructure shared files, implement sandbox verification --- src/cli/build.ts | 100 +++++++++--------- src/cli/cli.ts | 10 +- src/cli/{ => shared}/configuration.ts | 2 +- .../state.ts => shared/error-reporter.ts} | 26 ----- src/cli/shared/parser.ts | 26 ----- src/cli/tsconfig.json | 2 +- .../verify-sandboxes.ts | 55 ++-------- 7 files changed, 70 insertions(+), 151 deletions(-) rename src/cli/{ => shared}/configuration.ts (99%) rename src/cli/{verify-sandboxes/state.ts => shared/error-reporter.ts} (55%) delete mode 100644 src/cli/shared/parser.ts rename src/cli/{verify-sandboxes => }/verify-sandboxes.ts (74%) diff --git a/src/cli/build.ts b/src/cli/build.ts index ddc1aee3..d90a8cc9 100644 --- a/src/cli/build.ts +++ b/src/cli/build.ts @@ -3,62 +3,62 @@ import { StringBuilder } from './string-builder'; import * as fs from 'fs'; import * as path from 'path'; -export const build = (rootPath): Promise => { - let content = new StringBuilder(); - let home = path.resolve(rootPath); - let sandboxes = []; +export async function build(rootPath): Promise { + let content = new StringBuilder(); + let home = path.resolve(rootPath); + let sandboxes = []; - fromDir(home, /\.sandbox.ts$/, (filename) => { - let sandboxPath = filename.replace(home, '.').replace(/.ts$/, '').replace(/\\/g, '/'); - const contents = fs.readFileSync(filename, 'utf8'); + fromDir(home, /\.sandbox.ts$/, (filename) => { + let sandboxPath = filename.replace(home, '.').replace(/.ts$/, '').replace(/\\/g, '/'); + const contents = fs.readFileSync(filename, 'utf8'); - const matchSandboxOf = /\s?sandboxOf\s*\(\s*([^)]+?)\s*\)/g.exec(contents); - if (matchSandboxOf) { - const typeName = matchSandboxOf[1].split(',')[0].trim(); - const labelText = /label\s*:\s*['"](.+)['"]/g.exec(matchSandboxOf[0]); + const matchSandboxOf = /\s?sandboxOf\s*\(\s*([^)]+?)\s*\)/g.exec(contents); + if (matchSandboxOf) { + const typeName = matchSandboxOf[1].split(',')[0].trim(); + const labelText = /label\s*:\s*['"](.+)['"]/g.exec(matchSandboxOf[0]); - let scenarioMenuItems = []; - const scenarioRegex = /\.add\s*\(['"](.+)['"]\s*,\s*{/g; - let scenarioMatches; - let scenarioIndex = 1; - while ((scenarioMatches = scenarioRegex.exec(contents)) !== null) { - scenarioMenuItems.push({key: scenarioIndex, description: scenarioMatches[1]}); - scenarioIndex++; - } + let scenarioMenuItems = []; + const scenarioRegex = /\.add\s*\(['"](.+)['"]\s*,\s*{/g; + let scenarioMatches; + let scenarioIndex = 1; + while ((scenarioMatches = scenarioRegex.exec(contents)) !== null) { + scenarioMenuItems.push({ key: scenarioIndex, description: scenarioMatches[1] }); + scenarioIndex++; + } - let label = labelText ? labelText[1] : ''; - sandboxes.push({ - key: sandboxPath, - searchKey: `${typeName}${label}`, - name: typeName, - label: label, - scenarioMenuItems - }); - } - }); + let label = labelText ? labelText[1] : ''; + sandboxes.push({ + key: sandboxPath, + searchKey: `${typeName}${label}`, + name: typeName, + label: label, + scenarioMenuItems + }); + } + }); - content.addLine(`export function getSandboxMenuItems() {`); - content.addLine(`return ${JSON.stringify(sandboxes)};`); - content.addLine(`}`); + content.addLine(`export function getSandboxMenuItems() {`); + content.addLine(`return ${JSON.stringify(sandboxes)};`); + content.addLine(`}`); - content.addLine(`export function getSandbox(path: string) {`); - content.addLine(`switch(path) {`); - sandboxes.forEach(({key}) => { - content.addLine(`case '${key}':`); - content.addLine(`return import('${key}').then(sandbox => { return sandbox.default.serialize('${key}'); });`); - }); - content.addLine(`}}`); + content.addLine(`export function getSandbox(path: string) {`); + content.addLine(`switch(path) {`); + sandboxes.forEach(({ key }) => { + content.addLine(`case '${key}':`); + content.addLine(`return import('${key}').then(sandbox => { return sandbox.default.serialize('${key}'); });`); + }); + content.addLine(`}}`); - let filePath = path.resolve(home, './sandboxes.ts'); + let filePath = path.resolve(home, './sandboxes.ts'); - return new Promise((resolve, reject) => { - fs.writeFile(filePath, content.dump(), function (err) { - if (err) { - reject(err); - } else { - console.log(`Created file: ${filePath}`); - resolve(content); - } + return new Promise((resolve, reject) => { + fs.writeFile(filePath, content.dump(), function (err) { + if (err) { + reject(err); + } else { + console.log(`Created file: ${filePath}`); + resolve(filePath); + } + }); }); - }); -}; +} diff --git a/src/cli/cli.ts b/src/cli/cli.ts index 9febcf01..daf5eb9d 100644 --- a/src/cli/cli.ts +++ b/src/cli/cli.ts @@ -3,8 +3,8 @@ import * as path from 'path'; import { build } from './build'; import { startWatch } from './start-watch'; import { runAngularCli } from './run-angular-cli'; -import { getParsedArguments } from 'shared/parser'; -import { Configuration } from './configuration'; +import { Configuration } from './shared/configuration'; +import { verifySandboxes } from './verify-sandboxes'; (async () => { await run(); @@ -23,7 +23,7 @@ async function run() { process.exit(1); } - await build(playgroundConfig.sourceRoot); + const sandboxesPath = await build(playgroundConfig.sourceRoot); if (config.runWatch) { startWatch(playgroundConfig, () => build(playgroundConfig.sourceRoot)); @@ -32,4 +32,8 @@ async function run() { if (config.runAngularCliServe && playgroundConfig.angularCli) { runAngularCli(playgroundConfig.angularCli); } + + if (config.runCheckErrors) { + verifySandboxes(config, sandboxesPath); + } } diff --git a/src/cli/configuration.ts b/src/cli/shared/configuration.ts similarity index 99% rename from src/cli/configuration.ts rename to src/cli/shared/configuration.ts index 608b9021..d5ff3a91 100644 --- a/src/cli/configuration.ts +++ b/src/cli/shared/configuration.ts @@ -18,7 +18,7 @@ export class Configuration { runCheckErrors: boolean; randomScenario: boolean; configFilePath: string; - port = 4201; + port = 4201; // TODO timeoutAttempts = 20; chromeArguments = [ '--disable-gpu', '--no-sandbox' ]; diff --git a/src/cli/verify-sandboxes/state.ts b/src/cli/shared/error-reporter.ts similarity index 55% rename from src/cli/verify-sandboxes/state.ts rename to src/cli/shared/error-reporter.ts index 5f26b685..3b350b8c 100644 --- a/src/cli/verify-sandboxes/state.ts +++ b/src/cli/shared/error-reporter.ts @@ -1,27 +1,3 @@ -export interface ScenarioSummary { - url: string; - name: string; - description: string; -} - -export class Configuration { - public chromeArguments = [ - '--disable-gpu', - '--no-sandbox' - ]; - - constructor ( - public sandboxPath: string, - public buildMode: boolean, - public port: number, - public timeoutAttempts = 20 - ) {} - - get baseUrl(): string { - return `http://localhost:${this.port}`; - } -} - export enum ReportType { Log } @@ -35,8 +11,6 @@ export class ErrorReporter { return this._errors; } - - addError(error: any, scenario: string) { this._errors.push({ error, scenario }); } diff --git a/src/cli/shared/parser.ts b/src/cli/shared/parser.ts deleted file mode 100644 index 6064219e..00000000 --- a/src/cli/shared/parser.ts +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Separates accepted command line arguments from other ts-node arguments - * @param supportedFlags - Accepted command line flags - * @param args - Process arguments - */ -export function getParsedArguments(supportedFlags: string[], args: string[]) - : { flags: string[], args: string[] } { - const flags: string[] = []; - - args = args.reduce((accr, value) => { - const flag = getFlag(value); - supportedFlags.indexOf(flag) > -1 ? flags.push(value) : accr.push(value); - return accr; - }, []); - - return { flags, args }; -} - -/** - * Only grab flag from arguments with values - * e.g. --path=./src/ - * @param argument - Flag argument - */ -function getFlag(argument: string): string { - return argument.split('=')[0]; -} diff --git a/src/cli/tsconfig.json b/src/cli/tsconfig.json index fcca0924..1d315c1a 100644 --- a/src/cli/tsconfig.json +++ b/src/cli/tsconfig.json @@ -7,7 +7,7 @@ "moduleResolution": "node", "outDir": "../../dist/bin", "sourceMap": false, - "target": "es5", + "target": "es2015", "newLine": "LF", "typeRoots": [ "../../node_modules/@types" diff --git a/src/cli/verify-sandboxes/verify-sandboxes.ts b/src/cli/verify-sandboxes.ts similarity index 74% rename from src/cli/verify-sandboxes/verify-sandboxes.ts rename to src/cli/verify-sandboxes.ts index 9b1c204a..2140fa65 100644 --- a/src/cli/verify-sandboxes/verify-sandboxes.ts +++ b/src/cli/verify-sandboxes.ts @@ -1,56 +1,31 @@ -#! /usr/bin/env node import * as puppeteer from 'puppeteer'; import * as process from 'process'; import * as path from 'path'; -import { getParsedArguments } from '../shared/parser'; -import { Configuration, ScenarioSummary, ErrorReporter } from './state'; +import { ErrorReporter, ReportType } from './shared/error-reporter'; +import { Configuration } from './shared/configuration'; -// Parse command line input -const supportedFlages = ['--path', '--build', '--port']; -const parameters = process.argv.slice(2); -const parsedArguments = getParsedArguments(supportedFlages, parameters); +interface ScenarioSummary { + url: string; + name: string; + description: string; +} let browser: any; let currentScenario = ''; const reporter = new ErrorReporter(); -const SANDBOXES_PATH = './src/sandboxes.ts'; // Ensure Chromium instances are destroyed on err process.on('unhandledRejection', () => { if (browser) browser.close(); }); -// Begin browser tasks -// (async () => { -// await runPlayground(); -// await verifySandboxes(); -// })(); - -export async function verifySandboxes() { - const configuration = configure(parsedArguments.flags); - await main(configuration); +export async function verifySandboxes(configuration: Configuration, sandboxesPath: string) { + await main(configuration, sandboxesPath); } - ///////////////////////////////// -function configure(flags: string[]): Configuration { - const pathArg = flags.find(f => f.includes('--path')); - const portArg = flags.find(f => f.includes('--port')); - - if (!pathArg) { - console.error('Please specify the path to sandboxes.ts'); - process.exit(1); - } - - const sandboxLocation = path.join(process.cwd(), getArgumentValue(pathArg)); - const buildMode = flags.indexOf('--build') !== -1; - const port = portArg ? parseInt(getArgumentValue(portArg), 10) : 4201; - - return new Configuration(sandboxLocation, buildMode, port); -} - -async function main (configuration: Configuration) { +async function main (configuration: Configuration, sandboxesPath: string) { let timeoutAttempts = configuration.timeoutAttempts; browser = await puppeteer.launch({ headless: true, @@ -58,7 +33,7 @@ async function main (configuration: Configuration) { args: configuration.chromeArguments }); - const scenarios = getSandboxMetadata(configuration.baseUrl, configuration.buildMode, SANDBOXES_PATH); + const scenarios = getSandboxMetadata(configuration.baseUrl, configuration.randomScenario, sandboxesPath); console.log(`Retrieved ${scenarios.length} scenarios.\n`); for (let i = 0; i < scenarios.length; i++) { await openScenarioInNewPage(scenarios[i], configuration.timeoutAttempts); @@ -167,11 +142,3 @@ function onConsoleErr(msg: any) { function getRandomKey(menuItemsLength: number): number { return Math.floor(Math.random() * (menuItemsLength - 1) + 1); } - -/** - * Separates value of an argument from its flag - * @param argument - Flag with value e.g. --path=./src/ - */ -function getArgumentValue(argument: string) { - return argument.split('=')[1]; -} From be1f9d148fd1e749ff6b6569777a73bd793bbf9a Mon Sep 17 00:00:00 2001 From: mgmarlow Date: Tue, 28 Nov 2017 12:30:37 -0800 Subject: [PATCH 13/19] add ts-node dep for runtime TypeScript compilation --- package.json | 1 + src/cli/verify-sandboxes.ts | 3 +++ 2 files changed, 4 insertions(+) diff --git a/package.json b/package.json index d1a3fd2c..a795bfed 100644 --- a/package.json +++ b/package.json @@ -65,6 +65,7 @@ "gulp-inline-ng2-template": "^4.0.0", "puppeteer": "^0.13.0", "rxjs": "^5.5.2", + "ts-node": "^3.3.0", "tslint": "5.3.2", "typescript": "2.4.2", "zone.js": "0.8.4" diff --git a/src/cli/verify-sandboxes.ts b/src/cli/verify-sandboxes.ts index 2140fa65..def37aed 100644 --- a/src/cli/verify-sandboxes.ts +++ b/src/cli/verify-sandboxes.ts @@ -3,6 +3,9 @@ import * as process from 'process'; import * as path from 'path'; import { ErrorReporter, ReportType } from './shared/error-reporter'; import { Configuration } from './shared/configuration'; +// ts-node required for runtime typescript compilation of sandboxes.ts +require('ts-node/register'); + interface ScenarioSummary { url: string; From 5dece076da52f402c3d3fbacbb4b7b6b95a273b3 Mon Sep 17 00:00:00 2001 From: mgmarlow Date: Tue, 28 Nov 2017 12:46:55 -0800 Subject: [PATCH 14/19] add delay to chromium reconnection --- src/cli/cli.ts | 10 +++++----- src/cli/verify-sandboxes.ts | 9 +++++++++ 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/cli/cli.ts b/src/cli/cli.ts index daf5eb9d..8389b614 100644 --- a/src/cli/cli.ts +++ b/src/cli/cli.ts @@ -17,20 +17,20 @@ async function run() { let configFile = path.resolve(config.configFilePath); let playgroundConfig; try { - playgroundConfig = require(configFile.replace(/.json$/, '')); + playgroundConfig = require(configFile.replace(/.json$/, '')); } catch (e) { - process.stdout.write(`[angular-playground]: \x1b[31mFailed to load config file ${configFile}\x1b[0m\n`); - process.exit(1); + process.stdout.write(`[angular-playground]: \x1b[31mFailed to load config file ${configFile}\x1b[0m\n`); + process.exit(1); } const sandboxesPath = await build(playgroundConfig.sourceRoot); if (config.runWatch) { - startWatch(playgroundConfig, () => build(playgroundConfig.sourceRoot)); + startWatch(playgroundConfig, () => build(playgroundConfig.sourceRoot)); } if (config.runAngularCliServe && playgroundConfig.angularCli) { - runAngularCli(playgroundConfig.angularCli); + runAngularCli(playgroundConfig.angularCli); } if (config.runCheckErrors) { diff --git a/src/cli/verify-sandboxes.ts b/src/cli/verify-sandboxes.ts index def37aed..ea56ed6a 100644 --- a/src/cli/verify-sandboxes.ts +++ b/src/cli/verify-sandboxes.ts @@ -71,6 +71,7 @@ async function openScenarioInNewPage(scenario: ScenarioSummary, timeoutAttempts: try { await page.goto(scenario.url); } catch (e) { + await delay(5000); console.log(`Attempting to connect. (Attempts Remaining: ${timeoutAttempts})`); await openScenarioInNewPage(scenario, timeoutAttempts - 1); } @@ -145,3 +146,11 @@ function onConsoleErr(msg: any) { function getRandomKey(menuItemsLength: number): number { return Math.floor(Math.random() * (menuItemsLength - 1) + 1); } + +function delay(ms: number) { + return new Promise(resolve => { + setTimeout(() => { + resolve(); + }, ms); + }); +} From 3fedd9c47f26415d59469e375e353e0b52b554e5 Mon Sep 17 00:00:00 2001 From: mgmarlow Date: Tue, 28 Nov 2017 12:48:19 -0800 Subject: [PATCH 15/19] fix indentation --- src/cli/from-dir.ts | 24 +++++++-------- src/cli/run-angular-cli.ts | 62 +++++++++++++++++++------------------- src/cli/start-watch.ts | 14 ++++----- src/cli/string-builder.ts | 20 ++++++------ 4 files changed, 60 insertions(+), 60 deletions(-) diff --git a/src/cli/from-dir.ts b/src/cli/from-dir.ts index 8dd40b03..3dfd465b 100644 --- a/src/cli/from-dir.ts +++ b/src/cli/from-dir.ts @@ -2,17 +2,17 @@ import * as fs from 'fs'; import * as path from 'path'; export const fromDir = (startPath, filter, callback) => { - if (!fs.existsSync(startPath)) { - console.log("no dir ", startPath); - return; - } - let files = fs.readdirSync(startPath); - for (let i = 0; i < files.length; i++) { - let filename = path.join(startPath, files[i]); - let stat = fs.lstatSync(filename); - if (stat.isDirectory()) { - fromDir(filename, filter, callback); //recurse + if (!fs.existsSync(startPath)) { + console.log("no dir ", startPath); + return; + } + let files = fs.readdirSync(startPath); + for (let i = 0; i < files.length; i++) { + let filename = path.join(startPath, files[i]); + let stat = fs.lstatSync(filename); + if (stat.isDirectory()) { + fromDir(filename, filter, callback); //recurse + } + else if (filter.test(filename)) callback(filename); } - else if (filter.test(filename)) callback(filename); - } }; diff --git a/src/cli/run-angular-cli.ts b/src/cli/run-angular-cli.ts index 2ddd4bc4..34113a47 100644 --- a/src/cli/run-angular-cli.ts +++ b/src/cli/run-angular-cli.ts @@ -3,36 +3,36 @@ const path = require('path'); const childProcess = require('child_process'); export const runAngularCli = (angularCliConfig) => { - let port = angularCliConfig.port ? angularCliConfig.port : 4201; - let cliName = '@angular/cli'; - try{ - fs.accessSync(path.resolve('node_modules/@angular/cli/bin/ng')); - } catch (e) { - cliName = 'angular-cl'; - } - let cliPath = `node_modules/${cliName}/bin/ng`; - let args = [cliPath, 'serve', '-no-progress']; - args.push('--port'); - args.push(port.toString()); - if (angularCliConfig.appName) { - args.push(`-a=${angularCliConfig.appName}`); - } - if (angularCliConfig.environment) { - args.push(`-e=${angularCliConfig.environment}`); - } - if (angularCliConfig.args) { - args = args.concat(angularCliConfig.args); - } - const ngServe = childProcess.spawn('node', args, {maxBuffer: 1024 * 500}); - ngServe.stdout.on('data', (data) => { - write(process.stdout, data); - }); - ngServe.stderr.on('data', (data) => { - write(process.stderr, data); - }); + let port = angularCliConfig.port ? angularCliConfig.port : 4201; + let cliName = '@angular/cli'; + try { + fs.accessSync(path.resolve('node_modules/@angular/cli/bin/ng')); + } catch (e) { + cliName = 'angular-cl'; + } + let cliPath = `node_modules/${cliName}/bin/ng`; + let args = [cliPath, 'serve', '-no-progress']; + args.push('--port'); + args.push(port.toString()); + if (angularCliConfig.appName) { + args.push(`-a=${angularCliConfig.appName}`); + } + if (angularCliConfig.environment) { + args.push(`-e=${angularCliConfig.environment}`); + } + if (angularCliConfig.args) { + args = args.concat(angularCliConfig.args); + } + const ngServe = childProcess.spawn('node', args, { maxBuffer: 1024 * 500 }); + ngServe.stdout.on('data', (data) => { + write(process.stdout, data); + }); + ngServe.stderr.on('data', (data) => { + write(process.stderr, data); + }); - function write(handler, data) { - let message = data.toString(); - handler.write(`[ng serve]: ${message}\n`); - } + function write(handler, data) { + let message = data.toString(); + handler.write(`[ng serve]: ${message}\n`); + } }; diff --git a/src/cli/start-watch.ts b/src/cli/start-watch.ts index 6659d30a..94b16a88 100644 --- a/src/cli/start-watch.ts +++ b/src/cli/start-watch.ts @@ -2,12 +2,12 @@ const path = require('path'); const watch = require('node-watch'); export const startWatch = (config, cb) => { - let filter = (fn) => { - return (filename) => { - if (!/node_modules/.test(filename) && /\.sandbox.ts$/.test(filename)) { - fn(filename); - } + let filter = (fn) => { + return (filename) => { + if (!/node_modules/.test(filename) && /\.sandbox.ts$/.test(filename)) { + fn(filename); + } + }; }; - }; - watch([path.resolve(config.sourceRoot)], filter(cb)); + watch([path.resolve(config.sourceRoot)], filter(cb)); }; diff --git a/src/cli/string-builder.ts b/src/cli/string-builder.ts index c03e153d..1db6d145 100644 --- a/src/cli/string-builder.ts +++ b/src/cli/string-builder.ts @@ -1,14 +1,14 @@ export class StringBuilder { - private lines = []; + private lines = []; - addLine(line) { - this.lines.push(line); - } + addLine(line) { + this.lines.push(line); + } - dump() { - let data = this.lines.join('\n'); - data += '\n'; - this.lines = []; - return data; - } + dump() { + let data = this.lines.join('\n'); + data += '\n'; + this.lines = []; + return data; + } } From d1812bd5e6a8a348eb129603d4c0d84bf5afcc6c Mon Sep 17 00:00:00 2001 From: mgmarlow Date: Tue, 28 Nov 2017 12:57:44 -0800 Subject: [PATCH 16/19] ensure pages are closed during polling process --- src/cli/verify-sandboxes.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/cli/verify-sandboxes.ts b/src/cli/verify-sandboxes.ts index ea56ed6a..399a7f5d 100644 --- a/src/cli/verify-sandboxes.ts +++ b/src/cli/verify-sandboxes.ts @@ -69,15 +69,14 @@ async function openScenarioInNewPage(scenario: ScenarioSummary, timeoutAttempts: currentScenario = scenario.name; try { + console.log(`Checking: ${currentScenario}: ${scenario.description}`); await page.goto(scenario.url); } catch (e) { + await page.close(); await delay(5000); console.log(`Attempting to connect. (Attempts Remaining: ${timeoutAttempts})`); await openScenarioInNewPage(scenario, timeoutAttempts - 1); } - - console.log(`Checking: ${currentScenario}: ${scenario.description}`); - await page.close(); } /** From 34f4bfe7e22a8ada876c65df38332cde06947922 Mon Sep 17 00:00:00 2001 From: mgmarlow Date: Tue, 28 Nov 2017 12:58:50 -0800 Subject: [PATCH 17/19] update scripts --- examples/example-app-angular-cli/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/example-app-angular-cli/package.json b/examples/example-app-angular-cli/package.json index 0c94882c..3ed8a4ba 100644 --- a/examples/example-app-angular-cli/package.json +++ b/examples/example-app-angular-cli/package.json @@ -7,7 +7,7 @@ "ng": "ng", "playground:copy": "node ../../scripts/copy.js", "playground:run": "node ./node_modules/angular-playground/dist/bin/cli.js", - "playground:test": "node ./node_modules/angular-playground/dist/bin/verify-sandboxes/verify-sandboxes.js --path=./src/sandboxes.ts", + "playground:test": "node ./node_modules/angular-playground/dist/bin/cli.js --check-errors --random-scenario", "playground": "npm run playground:copy && npm run playground:run", "playground:dev": "npm run build --prefix ../../ && npm run playground", "playground2:run": "node ./node_modules/angular-playground/dist/bin/cli.js angular-playground2.json", From 3fb91c924606d5866e86126784f5cd735bd8dcb8 Mon Sep 17 00:00:00 2001 From: mgmarlow Date: Tue, 28 Nov 2017 13:02:42 -0800 Subject: [PATCH 18/19] update package.json cli flags --- examples/example-app-angular-cli/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/example-app-angular-cli/package.json b/examples/example-app-angular-cli/package.json index 3ed8a4ba..86551e33 100644 --- a/examples/example-app-angular-cli/package.json +++ b/examples/example-app-angular-cli/package.json @@ -12,7 +12,7 @@ "playground:dev": "npm run build --prefix ../../ && npm run playground", "playground2:run": "node ./node_modules/angular-playground/dist/bin/cli.js angular-playground2.json", "cli": "ts-node --project ../../src/cli ../../src/cli/cli.ts", - "cli:build": "ts-node --project ../../src/cli ../../src/cli/cli.ts -no-watch -no-serve", + "cli:build": "ts-node --project ../../src/cli ../../src/cli/cli.ts --no-watch --no-serve", "start": "ng serve -no-progress", "build": "ng build -prod -a=app", "lint": "ng lint", From 71f9be6f47956958c17c2a232525821150e15672 Mon Sep 17 00:00:00 2001 From: mgmarlow Date: Tue, 28 Nov 2017 14:27:09 -0800 Subject: [PATCH 19/19] add different port support --- src/cli/cli.ts | 1 + src/cli/shared/configuration.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cli/cli.ts b/src/cli/cli.ts index 8389b614..cbb54877 100644 --- a/src/cli/cli.ts +++ b/src/cli/cli.ts @@ -24,6 +24,7 @@ async function run() { } const sandboxesPath = await build(playgroundConfig.sourceRoot); + config.port = playgroundConfig.angularCli.port ? playgroundConfig.angularCli.port : 4201; if (config.runWatch) { startWatch(playgroundConfig, () => build(playgroundConfig.sourceRoot)); diff --git a/src/cli/shared/configuration.ts b/src/cli/shared/configuration.ts index d5ff3a91..d05d64fc 100644 --- a/src/cli/shared/configuration.ts +++ b/src/cli/shared/configuration.ts @@ -18,7 +18,7 @@ export class Configuration { runCheckErrors: boolean; randomScenario: boolean; configFilePath: string; - port = 4201; // TODO + port: number; timeoutAttempts = 20; chromeArguments = [ '--disable-gpu', '--no-sandbox' ];