diff --git a/.circleci/config.yml b/.circleci/config.yml index 7da621b6f..fa2aeeb31 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -7,12 +7,8 @@ jobs: steps: - checkout - run: - name: Install Dependencies - command: npm install - # test - - run: npm run test - # build - - run: npm run prod-webapp + name: Installing dependencies + command: pwd test-e2e-login: docker: - image: circleci/node:10.15.1-browsers @@ -20,17 +16,29 @@ jobs: steps: - checkout - run: - name: Repo Check 1 + name: Installing dependencies + command: npm install && cd ./test-e2e && npm install + - run: + name: Addittional step + command: pwd + - run: + name: Running Tests + command: | + export NODE_OPTIONS=--max_old_space_size=4096 + export CUCUMBER_TAG='@login' + export RANDOM_STRING='testing1' + npm run test-e2e:browserstack + - run: + name: Save test results command: | - if [ "$CIRCLE_REPOSITORY_URL" == "git@github.com:blockstack/blockstack-browser.git" ]; then - export TEST_E2E_GREP=login-to-hello-blockstack-app - npm install && npm run test-e2e:browserstack - else - export TEST_E2E_GREP=login-to-hello-blockstack-app - npm install && npm run test-e2e:localBuild - fi + tar -czvf test-e2e-login.tar.gz test-e2e/target/site/serenity + tar -czvf logs.tar.gz /home/circleci/.npm/_logs/ + mv logs.tar.gz test-e2e/target/logs.tar.gz + mv test-e2e-login.tar.gz test-e2e/target/test-e2e-login.tar.gz + when: on_fail - store_artifacts: - path: /tmp/test-errors + path: test-e2e/target + test-e2e-account-creation: docker: - image: circleci/node:10.15.1-browsers @@ -38,35 +46,82 @@ jobs: steps: - checkout - run: - name: Repo Check 2 + name: Installing dependencies + command: npm install && cd ./test-e2e && npm install + + - run: + name: Running Tests command: | - if [ "$CIRCLE_REPOSITORY_URL" == "git@github.com:blockstack/blockstack-browser.git" ]; then - export TEST_E2E_GREP=account-creation - npm install && npm run test-e2e:browserstack - else - export TEST_E2E_GREP=account-creation - npm install && npm run test-e2e:localBuild - fi + export NODE_OPTIONS=--max_old_space_size=4096 + export CUCUMBER_TAG='@accountCreation' + export RANDOM_STRING='testing2' + npm run test-e2e:browserstack + - run: + name: Save test results + command: | + tar -czvf test-e2e-account-creation.tar.gz test-e2e/target/site/serenity + tar -czvf logs.tar.gz /home/circleci/.npm/_logs/ + mv logs.tar.gz test-e2e/target/logs.tar.gz + mv test-e2e-account-creation.tar.gz test-e2e/target/test-e2e-account-creation.tar.gz + when: on_fail - store_artifacts: - path: /tmp/test-errors - test-e2e-account-recovery: + path: test-e2e/target + + test-e2e-account-recovery-via-magic-recovery-code: docker: - image: circleci/node:10.15.1-browsers working_directory: ~/repo steps: - checkout - run: - name: Repo Check 3 + name: Installing dependencies + command: npm install && cd ./test-e2e && npm install + + - run: + name: Running Tests + command: | + export NODE_OPTIONS=--max_old_space_size=4096 + export CUCUMBER_TAG='@magicRecovery' + export RANDOM_STRING='testing3' + npm run test-e2e:browserstack + - run: + name: Save test results + command: | + tar -czvf test-e2e-account-recovery-via-magic-recovery-code.tar.gz test-e2e/target/site/serenity + tar -czvf logs.tar.gz /home/circleci/.npm/_logs/ + mv logs.tar.gz test-e2e/target/logs.tar.gz + mv test-e2e-account-recovery-via-magic-recovery-code.tar.gz test-e2e/target/test-e2e-account-recovery-via-magic-recovery-code.tar.gz + when: on_fail + - store_artifacts: + path: test-e2e/target + + test-e2e-account-recovery-via-secret-key: + docker: + - image: circleci/node:10.15.1-browsers + working_directory: ~/repo + steps: + - checkout + - run: + name: Installing dependencies + command: npm install && cd ./test-e2e && npm install + + - run: + name: Running Tests + command: | + export NODE_OPTIONS=--max_old_space_size=4096 + export CUCUMBER_TAG='@secretRecovery' + export RANDOM_STRING='testing4' + npm run test-e2e:browserstack + - run: + name: Save test results command: | - if [ "$CIRCLE_REPOSITORY_URL" == "git@github.com:tim/blockstack-browser.git" ]; then - export TEST_E2E_GREP=account-recovery - npm install && npm run test-e2e:browserstack - else - export TEST_E2E_GREP=account-recovery - npm install && npm run test-e2e:localBuild - fi + tar -czvf test-e2e-account-recovery-via-secret-key.tar.gz test-e2e/target/site/serenity + tar -czvf logs.tar.gz /home/circleci/.npm/_logs/ + mv logs.tar.gz test-e2e/target/logs.tar.gz + mv test-e2e-account-recovery-via-secret-key.tar.gz test-e2e/target/test-e2e-account-recovery-via-secret-key.tar.gz + when: on_fail - store_artifacts: - path: /tmp/test-errors + path: test-e2e/target workflows: version: 2 build_and_test_e2e_local_or_remote-e2e: @@ -74,4 +129,5 @@ workflows: - build - test-e2e-login - test-e2e-account-creation - - test-e2e-account-recovery + - test-e2e-account-recovery-via-magic-recovery-code + - test-e2e-account-recovery-via-secret-key diff --git a/package-lock.json b/package-lock.json index 3f69c55c0..17289c986 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "blockstack-browser", - "version": "0.36.1", + "version": "0.36.3", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -1543,8 +1543,7 @@ "adm-zip": { "version": "0.4.11", "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.11.tgz", - "integrity": "sha512-L8vcjDTCOIJk7wFvmlEUN7AsSb8T+2JrdP7KINBjzr24TJ5Mwj590sLu3BC7zNZowvJWa/JtPmD8eJCzdtDWjA==", - "dev": true + "integrity": "sha512-L8vcjDTCOIJk7wFvmlEUN7AsSb8T+2JrdP7KINBjzr24TJ5Mwj590sLu3BC7zNZowvJWa/JtPmD8eJCzdtDWjA==" }, "agent-base": { "version": "4.2.1", @@ -1615,8 +1614,7 @@ "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" }, "ansi-styles": { "version": "3.2.1", @@ -1736,7 +1734,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", - "dev": true, "requires": { "array-uniq": "^1.0.1" } @@ -1744,8 +1741,7 @@ "array-uniq": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", - "dev": true + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=" }, "array-unique": { "version": "0.3.2", @@ -1764,6 +1760,11 @@ "function-bind": "^1.1.1" } }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=" + }, "asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", @@ -1821,8 +1822,7 @@ "assertion-error": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "dev": true + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==" }, "assign-symbols": { "version": "1.0.0", @@ -2097,8 +2097,7 @@ "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, "base": { "version": "0.11.2", @@ -2803,11 +2802,6 @@ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" }, - "bootstrap": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.2.1.tgz", - "integrity": "sha512-tt/7vIv3Gm2mnd/WeDx36nfGGHleil0Wg8IeB7eMrVkY0fZ5iTaBisSh8oNANc2IBsCc6vCgCNTIM/IEN0+50Q==" - }, "boxen": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz", @@ -2835,7 +2829,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2962,19 +2955,6 @@ "node-releases": "^1.1.3" } }, - "browserstack-local": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/browserstack-local/-/browserstack-local-1.3.7.tgz", - "integrity": "sha512-ilZlmiy7XYJxsztYan7XueHVr3Ix9EVh/mCiYN1G53wRPEW/hg1KMsseM6UExzVbexEqFEfwjkBLeFlSqxh+bQ==", - "dev": true, - "requires": { - "https-proxy-agent": "^2.2.1", - "is-running": "^2.0.0", - "ps-tree": "=1.1.1", - "sinon": "^1.17.6", - "temp-fs": "^0.9.9" - } - }, "bs58": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", @@ -3303,14 +3283,16 @@ } }, "chai": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-3.5.0.tgz", - "integrity": "sha1-TQJjewZ/6Vi9v906QOxW/vc3Mkc=", - "dev": true, + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", + "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", "requires": { - "assertion-error": "^1.0.1", - "deep-eql": "^0.1.3", - "type-detect": "^1.0.0" + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^3.0.1", + "get-func-name": "^2.0.0", + "pathval": "^1.1.0", + "type-detect": "^4.0.5" } }, "chain-function": { @@ -3353,8 +3335,7 @@ "check-error": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", - "dev": true + "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=" }, "check-types": { "version": "7.4.0", @@ -3433,19 +3414,6 @@ "tslib": "^1.9.0" } }, - "chromedriver": { - "version": "2.46.0", - "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-2.46.0.tgz", - "integrity": "sha512-dLtKIJW3y/PuFrPmcw6Mb8Nh+HwSqgVrK1rWgTARXhHfWvV822X2VRkx2meU/tg2+YQL6/nNgT6n5qWwIDHbwg==", - "dev": true, - "requires": { - "del": "^3.0.0", - "extract-zip": "^1.6.7", - "mkdirp": "^0.5.1", - "request": "^2.88.0", - "tcp-port-used": "^1.0.1" - } - }, "ci-info": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz", @@ -3722,8 +3690,7 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "concat-stream": { "version": "1.6.2", @@ -4058,13 +4025,12 @@ } }, "cross-env": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-5.2.0.tgz", - "integrity": "sha512-jtdNFfFW1hB7sMhr/H6rW1Z45LFqyI431m3qU6bFXcQ3Eh7LtBuG3h74o7ohHZ3crrRkkqHlo4jYHFPcjroANg==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-5.2.1.tgz", + "integrity": "sha512-1yHhtcfAd1r4nwQgknowuUNfIT9E8dOMMspC36g45dN+iD1blloi7xp8X/xAIDnjHWyt1uQ8PHk2fkNaym7soQ==", "dev": true, "requires": { - "cross-spawn": "^6.0.5", - "is-windows": "^1.0.0" + "cross-spawn": "^6.0.5" } }, "cross-fetch": { @@ -4474,20 +4440,11 @@ } }, "deep-eql": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", - "integrity": "sha1-71WKyrjeJSBs1xOQbXTlaTDrafI=", - "dev": true, + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", "requires": { - "type-detect": "0.1.1" - }, - "dependencies": { - "type-detect": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz", - "integrity": "sha1-C6XsKohWQORw6k6FBZcZANrFiCI=", - "dev": true - } + "type-detect": "^4.0.0" } }, "deep-equal": { @@ -4618,17 +4575,37 @@ } }, "del": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/del/-/del-3.0.0.tgz", - "integrity": "sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU=", - "dev": true, + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", + "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", "requires": { - "globby": "^6.1.0", + "globby": "^5.0.0", "is-path-cwd": "^1.0.0", "is-path-in-cwd": "^1.0.0", - "p-map": "^1.1.1", - "pify": "^3.0.0", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", "rimraf": "^2.2.8" + }, + "dependencies": { + "globby": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", + "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", + "requires": { + "array-union": "^1.0.1", + "arrify": "^1.0.0", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + } } }, "delayed-stream": { @@ -5126,8 +5103,7 @@ "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "escodegen": { "version": "1.8.1", @@ -5486,21 +5462,6 @@ "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", "dev": true }, - "event-stream": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", - "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=", - "dev": true, - "requires": { - "duplexer": "~0.1.1", - "from": "~0", - "map-stream": "~0.1.0", - "pause-stream": "0.0.11", - "split": "0.3", - "stream-combiner": "~0.0.4", - "through": "~2.3.1" - } - }, "eventemitter3": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-1.2.0.tgz", @@ -5812,18 +5773,6 @@ } } }, - "extract-zip": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.7.tgz", - "integrity": "sha1-qEC0uK9kAyZMjbV/Txp0Mz74H+k=", - "dev": true, - "requires": { - "concat-stream": "1.6.2", - "debug": "2.6.9", - "mkdirp": "0.5.1", - "yauzl": "2.4.1" - } - }, "extsprintf": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", @@ -5890,15 +5839,6 @@ } } }, - "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" - } - }, "figgy-pudding": { "version": "3.5.1", "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz", @@ -6320,12 +6260,6 @@ "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", "dev": true }, - "from": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", - "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=", - "dev": true - }, "from2": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", @@ -6377,8 +6311,7 @@ "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "fsevents": { "version": "1.2.7", @@ -6400,7 +6333,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -6421,12 +6355,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -6441,17 +6377,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -6568,7 +6507,8 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -6580,6 +6520,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -6594,6 +6535,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -6601,12 +6543,14 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -6625,6 +6569,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -6705,7 +6650,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -6717,6 +6663,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -6802,7 +6749,8 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -6838,6 +6786,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -6857,6 +6806,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -6900,12 +6850,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, @@ -7002,8 +6954,7 @@ "get-func-name": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", - "dev": true + "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=" }, "get-own-enumerable-property-symbols": { "version": "3.0.0", @@ -7098,7 +7049,6 @@ "version": "7.1.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", - "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -7305,7 +7255,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, "requires": { "ansi-regex": "^2.0.0" } @@ -8049,12 +7998,6 @@ "is-cwebp-readable": "^2.0.1" } }, - "immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=", - "dev": true - }, "immer": { "version": "1.12.1", "resolved": "https://registry.npmjs.org/immer/-/immer-1.12.1.tgz", @@ -8181,7 +8124,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, "requires": { "once": "^1.3.0", "wrappy": "1" @@ -8195,8 +8137,7 @@ "ini": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", - "dev": true + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" }, "inquirer": { "version": "6.2.2", @@ -8519,14 +8460,12 @@ "is-path-cwd": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", - "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", - "dev": true + "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=" }, "is-path-in-cwd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", - "dev": true, "requires": { "is-path-inside": "^1.0.0" } @@ -8535,7 +8474,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", - "dev": true, "requires": { "path-is-inside": "^1.0.1" } @@ -8598,12 +8536,6 @@ "integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ=", "dev": true }, - "is-running": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-running/-/is-running-2.1.0.tgz", - "integrity": "sha1-MKc/9cw4VOT8JUkICen1q/jeCeA=", - "dev": true - }, "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", @@ -8634,12 +8566,6 @@ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" }, - "is-url": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", - "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==", - "dev": true - }, "is-utf8": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", @@ -8658,17 +8584,6 @@ "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", "dev": true }, - "is2": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is2/-/is2-2.0.1.tgz", - "integrity": "sha512-+WaJvnaA7aJySz2q/8sLjMb2Mw14KTplHmSwcSpZ/fWJPkUmqw3YTzSWbPJ7OAwRvdYTWF2Wg+yYJ1AdP5Z8CA==", - "dev": true, - "requires": { - "deep-is": "^0.1.3", - "ip-regex": "^2.1.0", - "is-url": "^1.2.2" - } - }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -9228,53 +9143,6 @@ "integrity": "sha1-OGchPo3Xm/Ho8jAMDPwe+xgsDfE=", "dev": true }, - "jszip": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.1.5.tgz", - "integrity": "sha512-5W8NUaFRFRqTOL7ZDDrx5qWHJyBXy6velVudIzQUSoqAAYqzSh2Z7/m0Rf1QbmQJccegD0r+YZxBjzqoBiEeJQ==", - "dev": true, - "requires": { - "core-js": "~2.3.0", - "es6-promise": "~3.0.2", - "lie": "~3.1.0", - "pako": "~1.0.2", - "readable-stream": "~2.0.6" - }, - "dependencies": { - "core-js": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.3.0.tgz", - "integrity": "sha1-+rg/uwstjchfpjbEudNMdUIMbWU=", - "dev": true - }, - "es6-promise": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.0.2.tgz", - "integrity": "sha1-AQ1YWEI6XxGJeWZfRkhqlcbuK7Y=", - "dev": true - }, - "pako": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.8.tgz", - "integrity": "sha512-6i0HVbUfcKaTv+EG8ZTr75az7GFXcLYk9UyLEg7Notv/Ma+z/UG3TCoz6GiNeOrn1E/e63I0X/Hpw18jHOTUnA==", - "dev": true - }, - "readable-stream": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", - "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "~1.0.0", - "process-nextick-args": "~1.0.6", - "string_decoder": "~0.10.x", - "util-deprecate": "~1.0.1" - } - } - } - }, "key-encoder": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/key-encoder/-/key-encoder-1.1.7.tgz", @@ -9337,15 +9205,6 @@ "type-check": "~0.3.2" } }, - "lie": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", - "integrity": "sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=", - "dev": true, - "requires": { - "immediate": "~3.0.5" - } - }, "listenercount": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", @@ -9752,12 +9611,6 @@ "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", "dev": true }, - "map-stream": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", - "integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=", - "dev": true - }, "map-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", @@ -10048,7 +9901,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, "requires": { "brace-expansion": "^1.1.7" } @@ -10056,8 +9908,7 @@ "minimist": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" }, "minipass": { "version": "2.3.5", @@ -11006,7 +10857,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", - "dev": true, "requires": { "define-properties": "^1.1.2", "es-abstract": "^1.5.1" @@ -11066,7 +10916,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, "requires": { "wrappy": "1" } @@ -11250,12 +11099,6 @@ "p-limit": "^1.1.0" } }, - "p-map": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz", - "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==", - "dev": true - }, "p-map-series": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-map-series/-/p-map-series-1.0.0.tgz", @@ -11421,14 +11264,12 @@ "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "path-is-inside": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", - "dev": true + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=" }, "path-key": { "version": "2.0.1", @@ -11460,17 +11301,7 @@ "pathval": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", - "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", - "dev": true - }, - "pause-stream": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", - "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", - "dev": true, - "requires": { - "through": "~2.3" - } + "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=" }, "pbkdf2": { "version": "3.0.17", @@ -11504,14 +11335,12 @@ "pinkie": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", - "dev": true + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" }, "pinkie-promise": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", - "dev": true, "requires": { "pinkie": "^2.0.0" } @@ -11797,15 +11626,6 @@ "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", "dev": true }, - "ps-tree": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.1.1.tgz", - "integrity": "sha512-kef7fYYSKVqQffmzTMsVcUD1ObNJMp8sNSmHGlGKsZQyL/ht9MZKk86u0Rd1NhpTOAuhqwKCLLpktwkqz+MF8A==", - "dev": true, - "requires": { - "event-stream": "=3.3.4" - } - }, "pseudomap": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", @@ -11873,8 +11693,7 @@ "q": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", - "dev": true + "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=" }, "qr.js": { "version": "0.0.0", @@ -12936,7 +12755,6 @@ "version": "2.5.4", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.4.tgz", "integrity": "sha1-loAAk8vxoMhr2VtGJUZ1NcKd+gQ=", - "dev": true, "requires": { "glob": "^7.0.5" } @@ -13028,8 +12846,7 @@ "sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, "scheduler": { "version": "0.13.2", @@ -13090,29 +12907,6 @@ "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", "dev": true }, - "selenium-webdriver": { - "version": "4.0.0-alpha.1", - "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.0.0-alpha.1.tgz", - "integrity": "sha512-z88rdjHAv3jmTZ7KSGUkTvo4rGzcDGMq0oXWHNIDK96Gs31JKVdu9+FMtT4KBrVoibg8dUicJDok6GnqqttO5Q==", - "dev": true, - "requires": { - "jszip": "^3.1.3", - "rimraf": "^2.5.4", - "tmp": "0.0.30", - "xml2js": "^0.4.17" - }, - "dependencies": { - "tmp": { - "version": "0.0.30", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.30.tgz", - "integrity": "sha1-ckGdSovn1s51FI/YsyTlk6cRwu0=", - "dev": true, - "requires": { - "os-tmpdir": "~1.0.1" - } - } - } - }, "selfsigned": { "version": "1.10.4", "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.4.tgz", @@ -13780,15 +13574,6 @@ } } }, - "split": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", - "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=", - "dev": true, - "requires": { - "through": "2" - } - }, "split-on-first": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", @@ -13917,15 +13702,6 @@ "readable-stream": "^2.0.2" } }, - "stream-combiner": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", - "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=", - "dev": true, - "requires": { - "duplexer": "~0.1.1" - } - }, "stream-each": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", @@ -14050,7 +13826,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, "requires": { "ansi-regex": "^2.0.0" } @@ -14310,48 +14085,12 @@ "xtend": "^4.0.0" } }, - "tcp-port-used": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tcp-port-used/-/tcp-port-used-1.0.1.tgz", - "integrity": "sha512-rwi5xJeU6utXoEIiMvVBMc9eJ2/ofzB+7nLOdnZuFTmNCLqRiQh2sMG9MqCxHU/69VC/Fwp5dV9306Qd54ll1Q==", - "dev": true, - "requires": { - "debug": "4.1.0", - "is2": "2.0.1" - }, - "dependencies": { - "debug": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.0.tgz", - "integrity": "sha512-heNPJUJIqC+xB6ayLAMHaIrmN9HKa7aQO8MGqKpvCA+uJYVcvR6l5kgdrhRuwPFHU7P5/A1w0BjByPHwpfTDKg==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - } - } - }, "temp-dir": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz", "integrity": "sha1-CnwOom06Oa+n4OvqnB/AvE2qAR0=", "dev": true }, - "temp-fs": { - "version": "0.9.9", - "resolved": "https://registry.npmjs.org/temp-fs/-/temp-fs-0.9.9.tgz", - "integrity": "sha1-gHFzBDeHByDpQxUy/igUNk+IA9c=", - "dev": true, - "requires": { - "rimraf": "~2.5.2" - } - }, "tempfile": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/tempfile/-/tempfile-2.0.0.tgz", @@ -14931,10 +14670,9 @@ } }, "type-detect": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-1.0.0.tgz", - "integrity": "sha1-diIXzAbbJY7EiQihKY6LlRIejqI=", - "dev": true + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==" }, "type-is": { "version": "1.6.16", @@ -15330,7 +15068,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", - "dev": true, "requires": { "define-properties": "^1.1.2", "object.getownpropertydescriptors": "^2.0.3" @@ -15435,6 +15172,48 @@ "minimalistic-assert": "^1.0.0" } }, + "webdriver-manager": { + "version": "12.1.7", + "resolved": "https://registry.npmjs.org/webdriver-manager/-/webdriver-manager-12.1.7.tgz", + "integrity": "sha512-XINj6b8CYuUYC93SG3xPkxlyUc3IJbD6Vvo75CVGuG9uzsefDzWQrhz0Lq8vbPxtb4d63CZdYophF8k8Or/YiA==", + "requires": { + "adm-zip": "^0.4.9", + "chalk": "^1.1.1", + "del": "^2.2.0", + "glob": "^7.0.3", + "ini": "^1.3.4", + "minimist": "^1.2.0", + "q": "^1.4.1", + "request": "^2.87.0", + "rimraf": "^2.5.2", + "semver": "^5.3.0", + "xml2js": "^0.4.17" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + } + } + }, "webidl-conversions": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", @@ -16615,8 +16394,7 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "write": { "version": "1.0.3", @@ -16654,20 +16432,19 @@ "dev": true }, "xml2js": { - "version": "0.4.19", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", - "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", - "dev": true, + "version": "0.4.22", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.22.tgz", + "integrity": "sha512-MWTbxAQqclRSTnehWWe5nMKzI3VmJ8ltiJEco8akcC6j3miOhjjfzKum5sId+CWhfxdOs/1xauYr8/ZDBtQiRw==", "requires": { "sax": ">=0.6.0", - "xmlbuilder": "~9.0.1" + "util.promisify": "~1.0.0", + "xmlbuilder": "~11.0.0" } }, "xmlbuilder": { - "version": "9.0.7", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", - "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=", - "dev": true + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" }, "xtend": { "version": "4.0.1", @@ -16826,15 +16603,6 @@ "lodash.assign": "^4.0.6" } }, - "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" - } - }, "yup": { "version": "0.24.1", "resolved": "https://registry.npmjs.org/yup/-/yup-0.24.1.tgz", diff --git a/package.json b/package.json index d76e0e312..a4658c086 100644 --- a/package.json +++ b/package.json @@ -7,11 +7,12 @@ "bigi": "^1.4.2", "bip39": "^2.2.0", "bitcoinjs-lib": "^3.2.0", - "blockstack-ui": "^0.0.71", "blockstack": "^19.1.0", + "blockstack-ui": "^0.0.71", "body-parser": "^1.16.1", - "bootstrap": "^4.0.0-beta", + "bootstrap": "^4.3.1", "browser-stdout": "^1.3.0", + "chai": "^4.2.0", "chroma-js": "^1.3.7", "clean-tag": "^2.0.3", "core-js": "^2.6.4", @@ -25,7 +26,7 @@ "is-retina": "^1.0.3", "isomorphic-fetch": "^2.2.1", "jsontokens": "^0.7.8", - "lodash": "^4.17.10", + "lodash": "^4.17.15", "log4js": "^3.0.4", "mdi-react": "^5.2.0", "memoize-one": "^5.0.0", @@ -62,6 +63,7 @@ "styled-system": "^3.2.1", "system-components": "^3.0.3", "triplesec": "^3.0.25", + "webdriver-manager": "^12.1.7", "yup": "^0.24.1", "zone-file": "^0.2.2" }, @@ -97,12 +99,9 @@ "babel-plugin-module-resolver": "^3.2.0", "babel-plugin-rewire": "^1.2.0", "babel-plugin-styled-components": "^1.10.0", - "browserstack-local": "^1.3.7", - "chai": "^3.5.0", - "chromedriver": "^2.46.0", "copy-webpack-plugin": "^4.5.2", "cors": "^2.8.1", - "cross-env": "^5.2.0", + "cross-env": "^5.2.1", "css-loader": "^2.1.1", "enzyme": "^3.3.0", "eslint": "^5.13.0", @@ -138,7 +137,6 @@ "redux-mock-store": "^1.2.3", "request": "^2.88.0", "require-hacker": "^3.0.1", - "selenium-webdriver": "^4.0.0-alpha.1", "serve": "^10.1.2", "serve-handler": "^5.0.8", "shx": "^0.3.2", @@ -219,8 +217,8 @@ "start": "serve build --single", "web": "npm run build", "prod-webapp": "npm run clean && cross-env NODE_ENV=production WEBAPP=true webpack", - "test-e2e": "cross-env-shell mocha --opts test-e2e/mocha.opts test-e2e --grep=$TEST_E2E_GREP", - "test-e2e:localBuild": "cross-env NODE_ENV=production WEBAPP=true DEBUG_LOGGING=true webpack && E2E_BROWSER_HOST='./build' npm run test-e2e", - "test-e2e:browserstack": "cross-env USE_BROWSERSTACK=true npm run test-e2e:localBuild" + "test-e2e": "cd ./test-e2e && npm run test-e2e", + "test-e2e:localBuild": "cross-env NODE_ENV=production WEBAPP=true DEBUG_LOGGING=true webpack && cross-env E2E_BROWSER_HOST='../build' npm run test-e2e", + "test-e2e:browserstack": "cross-env NODE_ENV=production WEBAPP=true DEBUG_LOGGING=true webpack && cd ./test-e2e && cross-env E2E_BROWSER_HOST='../build' USE_BROWSERSTACK=true npm run test-e2e:browserstack" } } diff --git a/test-e2e/README.md b/test-e2e/README.md old mode 100644 new mode 100755 index 78f04af68..8750dada9 --- a/test-e2e/README.md +++ b/test-e2e/README.md @@ -1,67 +1,21 @@ +##Setup +Install node dependencies in root level and test-e2e folder: `npm install` + ## Running Tests These test can be ran using the web browsers on your local machine or using BrowserStack's environments. The tests can be targeting against any provided hosted browser endpoint as well as a locally built & hosted endpoint. +#### Using production endpoint and local browsers +Use `npm run test-e2e` to run tests using your local system web browsers and against the production endpoint. -Use `npm run test-e2e:localBuild` to build and host the browser locally, then run tests using your local system web browsers. - -Use `npm run test-e2e:browserstack` to build and host the browser locally, then run tests against BrowserStack's grid. All major operating systems and browsers are setup and working, including iOS and Android. - -To specify an endpoint and BrowserStack usage, use `npm run test-e2e` with environment variables `E2E_BROWSER_HOST={url|directory}` and `USE_BROWSERSTACK={true|false}`. - -If `E2E_BROWSER_HOST` is set and is not an `http(s)://` url, it is assumed to be a local file system directory, and a local static web server will be spawned and pointed at the directory. - -#### Run specific test suite(s) - -Use the environment variable `TEST_E2E_GREP` to target specific test suites and/or web browsers / environments. This uses [Mocha's `--grep` feature](https://mochajs.org/#-grep-regexp-g-regexp) which supports regex. Test suite all have the naming structure `{description} {web browser / environment}`. - -Examples: -* Run all test suites locally on only the Chrome browser: - * `TEST_E2E_GREP=chrome npm run test-e2e:localBuild` -* Run the login test suite locally on only the Chrome browser: - * `TEST_E2E_GREP=login-to-hello-blockstack-app.*chrome npm run test-e2e:localBuild` -* Run all test suites against the remote BrowserStack iOS platforms: - * `TEST_E2E_GREP=iOS npm run test-e2e:browserstack` -* Run the login test suite against the remote BrowserStack iOS platforms: - * `TEST_E2E_GREP=login-to-hello-blockstack-app.*iOS npm run test-e2e:browserstack` - - +#### Using local static web server and local browsers +Use `npm run test-e2e:localBuild` to build and host the local static web server, then run tests using your local system web browsers. #### Using BrowserStack - In order to run tests against BrowserStack, auth credentials must be specified in the environmental variable `BROWSERSTACK_AUTH` with the format `"user:key"`. - For example `BROWSERSTACK_AUTH="alice1:yUDBktWP1tRdrfq5Lpck"`. +Use `npm run test-e2e:browserstack` to build and host the browser locally, then run tests against BrowserStack's grid. All major operating systems and browsers are setup and working, including iOS and Android. -#### Running against the production name registrar - -By default, the account creation tests will use the `test-registrar.blockstack.org` name registrar with the `.test-personal.id` domain suffix. This is to avoid spamming `.blockstack.id` with test IDs. - -The production name registrar can be enabled by setting the environment variable `TEST_PRODUCTION_REGISTRAR`. - - -## Writing new test suites - -Use `createTestSuites({test suite description}, ... )` which takes care of initializing a selenium WebDriver for each web browser environment. For example: - -```js -const createTestSuites = require('../utils/create-test-suites'); - -createTestSuites('account recovery via secret key', ({ driver, browserHostUrl }) => { - - step('load initial page', async () => { - await driver.get(browserHostUrl); - }); - - step('load sign in page', async () => { - await driver.click(By.xpath('//a[contains(.,"Sign in with an existing ID")]')); - }); - - step('enter secret recovery key', async () => { - await driver.setText(By.css('textarea[name="recoveryKey"]'), SECRET_RECOVERY_KEY); - await driver.click(By.css('button[type="submit"]')); - }); - -}); -``` +## HTML Report +`index.html` Html report is generated in the 'test-e2e\target\site\serenity' folder. diff --git a/test-e2e/auth/create-account.js b/test-e2e/auth/create-account.js deleted file mode 100644 index 59649d142..000000000 --- a/test-e2e/auth/create-account.js +++ /dev/null @@ -1,121 +0,0 @@ -const { WebDriver, Builder, By, Key, until } = require('selenium-webdriver'); -const { expect } = require('chai'); -const createTestSuites = require('../utils/create-test-suites'); -const helpers = require('../utils/helpers'); - -// selenium-webdriver docs: https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/webdriver.html - -createTestSuites('account-creation', ({driver, browserHostUrl}) => { - - step('load initial page', async () => { - await driver.get(browserHostUrl); - await driver.el(By.xpath('//*[contains(.,"Create your Blockstack ID")]')); - }); - - if (!process.env['TEST_PRODUCTION_REGISTRAR']) { - step('set "test-registrar.blockstack.org" as API endpoint for ID registration', async () => { - await driver.retry(async () => { - await driver.executeScript(`window.SUBDOMAIN_SUFFIX_OVERRIDE = "test-personal.id";`); - }, 10000, 200); - }); - } - - step('load create new ID page', async () => { - await driver.click(By.xpath('//div[text()="Create new ID"]')); - }); - - let randomUsername - step('enter unique username', async () => { - randomUsername = `test_e2e_${Date.now() / 100000 | 0}_${helpers.getRandomInt(100000, 999999)}`; - await driver.setText(By.css('input[type="text"][name="username"]'), randomUsername); - await driver.click(By.xpath('//button[contains(., "Check Availability")]')); - await driver.click(By.xpath('//button[contains(., "Continue")]')); - }); - - step('enter password', async () => { - const randomPassword = helpers.getRandomString(); - await driver.setText(By.css('input[type="password"][name="password"]'), randomPassword); - await driver.setText(By.css('input[type="password"][name="passwordConfirm"]'), randomPassword); - await driver.click(By.xpath('//button[contains(., "Register ID")]')); - }); - - step('wait for creating Blockstack ID spinner', async () => { - try { - // first check if message is still showing (it may have been quick and already closed) - await driver.wait(until.elementLocated(By.xpath('//*[contains(text(), "Creating your Blockstack ID")]')), 2500); - } catch (err) { - console.warn(`Error checking for "Creating your Blockstack ID" spinner: ${err}`); - } - // wait for next page to load - await driver.el(By.xpath('//*[contains(text(), "What is your email")]'), null, - { timeout: 90000, poll: 200, driverWait: 90000 }); - }); - - step('enter email', async () => { - await driver.setText(By.css('input[type="email"][name="email"]'), `${randomUsername}@none.test`); - await driver.click(By.xpath('//button[contains(., "Next")]')); - }); - - step('expect recovery email to fail', async () => { - // Expect recovery email delivery to fail (the email domain does not exist) - await driver.el(By.xpath('//*[contains(., "email failed")]')); - }); - - step('check username registration failed', async () => { - try { - await driver.wait(until.elementLocated(By.xpath('//*[text()="Username Registration Failed"]')), 2500); - console.warn('Username Registration Failed - does test server IP need whitelisted on the registar?') - // close the alert since it can eclipse the continuation button.. - const el = await driver.click(By.xpath('//*[text()="Username Registration Failed"]/parent::div/following-sibling::div/descendant::span')); - await driver.wait(until.elementIsNotVisible(el)); - } catch (err) { - /* ignore error, this is expected if registration is successful */ - } - }); - - step('acknowledge saving recovery key phrase', async () => { - // First click redirects the page from /sign-up to /seed but doesn't change the form - await driver.click(By.xpath('//div[text()="Secret Recovery Key"]/parent::div')); - await driver.el(By.xpath('//div[contains(., "Save your Secret Recovery")]')); - await driver.click(By.xpath('//div[text()="Secret Recovery Key"]/parent::div')); - }); - - step('wait for "unlocking recovery key"', async () => { - try { - // first check if message is still showing (it may have been quick and already closed) - await driver.wait(until.elementLocated(By.xpath('//*[contains(text(), "Unlocking Recovery Key")]')), 2500); - } catch (err) { - console.warn(`Error checking for "Unlocking Recovery Key" spinner: ${err}`); - } - // wait for next page to load - await driver.el(By.xpath('//*[text()="Your Secret Recovery Key"]/following-sibling::*'), null, - { timeout: 90000, poll: 200, driverWait: 90000 }); - }); - - let keyWords; - step('get secret recovery key phrase', async () => { - // Get the recovery key phrase - const keyEl = await driver.el(By.xpath('//*[text()="Your Secret Recovery Key"]/following-sibling::*')); - keyWords = await keyEl.getText(); - keyWords = keyWords.trim().split(' '); - expect(keyWords).lengthOf(12, 'Recovery key phrase should be 12 words'); - await driver.click(By.xpath('//div[text()="Continue"]/parent::div')); - }); - - step('perform recovery key phrase verification instructions', async () => { - await driver.sleep(2500); // wait for animation - const selectWordsEl = await driver.el(By.xpath('//*[contains(text(), "Select words #")]')); - let selectWords = await selectWordsEl.getText(); - // Parse the phrase word numbers to validate - selectWords = selectWords.match(/#([0-9]+)/g).map(s => keyWords[parseInt(s.slice(1)) - 1]); - for (let selectWord of selectWords) { - await driver.click(By.xpath(`//div[span[text()="${selectWord}"]]`)); - } - }); - - step('load main page as authenticated user', async () => { - await driver.click(By.xpath('//div[text()="Go to Blockstack"]')); - await driver.el(By.xpath('//*[text()="Top Apps"]')); - }); - -}); diff --git a/test-e2e/auth/recover-account-magic-recovery.js b/test-e2e/auth/recover-account-magic-recovery.js deleted file mode 100644 index 9a87a4c26..000000000 --- a/test-e2e/auth/recover-account-magic-recovery.js +++ /dev/null @@ -1,72 +0,0 @@ -const { By, until } = require('selenium-webdriver'); -const createTestSuites = require('../utils/create-test-suites'); -const SAMPLE_ACCOUNT = require('./sample-account'); - -createTestSuites('account-recovery-via-magic-recovery-code', ({driver, browserHostUrl}) => { - - step('load initial page', async () => { - await driver.get(browserHostUrl); - await driver.el(By.xpath('//*[contains(.,"Create your Blockstack ID")]')); - }); - - step('load sign in page', async () => { - await driver.click(By.xpath('//a[contains(.,"Sign in with an existing ID")]')); - }); - - step('enter secret recovery key', async () => { - await driver.setText(By.css('textarea[name="recoveryKey"]'), SAMPLE_ACCOUNT.MAGIC_RECOVERY_CODE); - await driver.click(By.css('button[type="submit"]')); - }); - - step('enter password', async () => { - await driver.setText(By.css('input[name="password"]'), SAMPLE_ACCOUNT.PASSWORD); - await driver.click(By.css('button[type="submit"]')); - }); - - step('wait for "Loading" spinner', async () => { - try { - // first check if message is still showing (it may have been quick and already closed) - await driver.wait(until.elementLocated(By.xpath('//*[contains(text(), "Loading")]')), 2500); - } catch (err) { - console.warn(`Ignoring error checking for "Loading" spinner: ${err}`); - } - - // wait for next page to load - await driver.el(By.xpath('//*[contains(text(), "What is your email")]'), null, - { timeout: 150000, poll: 200, driverWait: 150000 }); - }); - - step('enter email', async () => { - await driver.setText(By.css('input[name="email"]'), SAMPLE_ACCOUNT.EMAIL); - await driver.click(By.css('button[type="submit"]')); - }); - - step('wait for "Restoring your Blockstack ID"', async () => { - try { - // first check if message is still showing (it may have been quick and already closed) - await driver.wait(until.elementLocated(By.xpath('//*[contains(text(), "Restoring your Blockstack ID")]')), 2500); - } catch (err) { - console.warn(`Ignoring error checking for "Restoring your Blockstack ID" spinner: ${err}`); - } - // wait for next page to load - await driver.el(By.xpath('//*[contains(.,"Go to Blockstack")]'), null, - { timeout: 90000, poll: 200, driverWait: 90000 }); - }); - - step('load main page as authenticated user', async () => { - // This doesn't work on mobile (appium) - no idea why.. - // await driver.click(By.xpath('//div[text()="Go to Blockstack"]')); - await driver.executeScript(` - var elements = document.getElementsByTagName("div"); - for (var i = 0; i < elements.length; i++) { - if (elements[i].innerText == "Go to Blockstack") { - elements[i].click(); - return; - } - } - throw new Error("not found"); - `); - await driver.el(By.xpath('//*[text()="Top Apps"]')); - }); - -}); diff --git a/test-e2e/auth/recover-account-secret-key.js b/test-e2e/auth/recover-account-secret-key.js deleted file mode 100644 index dc1276af0..000000000 --- a/test-e2e/auth/recover-account-secret-key.js +++ /dev/null @@ -1,61 +0,0 @@ -const { By, until } = require('selenium-webdriver'); -const createTestSuites = require('../utils/create-test-suites'); -const SAMPLE_ACCOUNT = require('./sample-account'); - -createTestSuites('account-recovery-via-secret-key', ({driver, browserHostUrl }) => { - - step('load initial page', async () => { - await driver.get(browserHostUrl); - await driver.el(By.xpath('//*[contains(.,"Create your Blockstack ID")]')); - }); - - step('load sign in page', async () => { - await driver.click(By.xpath('//a[contains(.,"Sign in with an existing ID")]')); - }); - - step('enter secret recovery key', async () => { - await driver.setText(By.css('textarea[name="recoveryKey"]'), SAMPLE_ACCOUNT.SECRET_RECOVERY_KEY); - await driver.click(By.css('button[type="submit"]')); - }); - - step('create password', async () => { - await driver.setText(By.css('input[name="password"]'), SAMPLE_ACCOUNT.PASSWORD); - await driver.setText(By.css('input[name="passwordConfirm"]'), SAMPLE_ACCOUNT.PASSWORD); - await driver.click(By.css('button[type="submit"]')); - }); - - step('enter email', async () => { - await driver.setText(By.css('input[name="email"]'), SAMPLE_ACCOUNT.EMAIL); - await driver.click(By.css('button[type="submit"]')); - }); - - step('wait for "Restoring your Blockstack ID"', async () => { - try { - // first check if message is still showing (it may have been quick and already closed) - await driver.wait(until.elementLocated(By.xpath('//*[contains(text(), "Restoring your Blockstack ID")]')), 2500); - } catch (err) { - console.warn(`Ignoring error checking for "Restoring your Blockstack ID" spinner: ${err}`); - } - // wait for next page to load - await driver.el(By.xpath('//*[contains(.,"Go to Blockstack")]'), null, - { timeout: 150000, poll: 200, driverWait: 150000 }); - }); - - step('load main page as authenticated user', async () => { - // This doesn't work on mobile (appium) - no idea why.. - // await driver.click(By.xpath('//div[text()="Go to Blockstack"]')); - await driver.executeScript(` - var elements = document.getElementsByTagName("div"); - for (var i = 0; i < elements.length; i++) { - if (elements[i].innerText == "Go to Blockstack") { - elements[i].click(); - return; - } - } - throw new Error("not found"); - `); - - await driver.el(By.xpath('//*[text()="Top Apps"]')); - }); - -}); diff --git a/test-e2e/auth/sample-account.js b/test-e2e/auth/sample-account.js deleted file mode 100644 index dd18ec85f..000000000 --- a/test-e2e/auth/sample-account.js +++ /dev/null @@ -1,14 +0,0 @@ -module.exports = { - RECOVERY_ID: 'test_e2e_recovery', - PASSWORD: '7p7M4vu89xMn964AE6T7', - EMAIL: 'test_e2e_recovery@mail-apps.com', - SECRET_RECOVERY_KEY: 'layer decrease junk moral access kid say model enter rigid spend simple', - MAGIC_RECOVERY_CODE: '6SEAA7SaeQKTnrkcr2CBxRpD8ZeFj7oYLeysPG9Mv7Ibp7Jq5Wie1vLn3fX2ZSMEcs8aXDrSlx6Eso3TWiM+DJA3C9/EabxQqeXvyjcolok=', - - /** - * This is just a dump the localStorage `redux` object from the signed in sample account. - * Allows rapid login for this test suite. This will need updated occasionally after changes to - * this object schema. Does not include the lazy-loaded app list data. - */ - LOCAL_STORAGE_DATA: "{\"account\":{\"accountCreated\":true,\"promptedForEmail\":false,\"email\":\"test_e2e_recovery@mail-apps.com\",\"encryptedBackupPhrase\":\"d91fa4ece7417c78f7ff22d4a93c519f0b6786e91348ec88b407dfb99a4463122d7049225bad239f39cb7720d21187a0631c6524b6077453bca6705d1f7a014e7b6c9f85474e5af104f3210681606f1d\",\"identityAccount\":{\"publicKeychain\":\"xpub6BD5AWkvt1ZPPvT7hbYw7mBU2fnsBZaw7q3ojAvnsGBPJLGLwuCJj7jLVi6XGhkz5cpLqRW9rnVBw1U41ckdZdS1bVYqTcnjKMUuRq45a88\",\"addresses\":[\"1NDsatzAEqrErxkB1osfJXouADgrHXuDs1\"],\"keypairs\":[{\"key\":\"2680e401fe670c15c43ad9686395177dea7a5f006848eb8eaf4729fc9ec289ee\",\"keyID\":\"03221818a0a3b3f9e3b0b369f085904c63c6caa506a3e1d28dcbc0d6a9aa7a82d2\",\"address\":\"1NDsatzAEqrErxkB1osfJXouADgrHXuDs1\",\"appsNodeKey\":\"xprvA2FnvytPSbUWyyrt3Bwxfaf6urjgAPHqaZZbNiXvE8HUZ4kRAvwNC1hkpjzsf1YnzR8e9SC8CXNBiLG4ZF1oPy3i5eVE5iEVU6YZVspN6tE\",\"salt\":\"19bc80bb27060f30fa4f05b07c92069c3633d6a70b089046f5605329fcdec798\"}],\"addressIndex\":1},\"bitcoinAccount\":{\"publicKeychain\":\"xpub6D1QcTvSqDXUsFF6n35HTohhgzov6LY9fRzNZF17MRTPzdvvN5NTtpUHPXujBEg1i1LFFcbdaFhiQiMgeWkjbCgZaAEB2cdcDxenpwnwpqd\",\"addresses\":[\"16ZsuXRZUipZHdrNPpkpDCJF8ZcwsssGbE\"],\"addressIndex\":0,\"balances\":{\"total\":0}},\"coreWallet\":{\"address\":null,\"balance\":0,\"withdrawal\":{\"txHex\":null,\"isBuilding\":false,\"isBroadcasting\":false,\"inProgress\":false,\"error\":null,\"recipientAddress\":null,\"success\":false}},\"viewedRecoveryCode\":false,\"recoveryCodeVerified\":false,\"connectedStorageAtLeastOnce\":true},\"auth\":{\"appManifest\":null,\"appManifestLoaded\":false,\"appManifestLoading\":false,\"appManifestLoadingError\":null,\"coreSessionTokens\":{},\"loggedIntoApp\":false},\"profiles\":{\"availability\":{\"names\":{},\"lastNameEntered\":null},\"identity\":{\"default\":0,\"localIdentities\":[{\"username\":\"test_e2e_recovery.id.blockstack\",\"usernameOwned\":true,\"usernamePending\":false,\"profile\":{\"@type\":\"Person\",\"@context\":\"http:\/\/schema.org\",\"api\":{\"gaiaHubConfig\":{\"url_prefix\":\"https:\/\/gaia.blockstack.org\/hub\/\"},\"gaiaHubUrl\":\"https:\/\/hub.blockstack.org\"},\"name\":\"Alice Devname\"},\"verifications\":[],\"trustLevel\":0,\"registered\":false,\"ownerAddress\":\"1NDsatzAEqrErxkB1osfJXouADgrHXuDs1\",\"zoneFile\":\"$ORIGIN test_e2e_recovery.id.blockstack\\n$TTL 3600\\n_http._tcp\\tIN\\tURI\\t10\\t1\\t\\\"https:\/\/gaia.blockstack.org\/hub\/1NDsatzAEqrErxkB1osfJXouADgrHXuDs1\/profile.json\\\"\\n\\n\"}],\"publicIdentities\":{},\"nameTransfers\":[],\"zoneFileUpdates\":[],\"createProfileError\":null,\"isProcessing\":false},\"pgp\":{\"publicKeys\":{}},\"registration\":{},\"search\":{\"query\":\"\",\"results\":[]}},\"sanity\":{\"coreApiRunning\":true,\"coreApiPasswordValid\":true},\"settings\":{\"api\":{\"apiCustomizationEnabled\":true,\"nameLookupUrl\":\"https:\/\/core.blockstack.org\/v1\/names\/{name}\",\"searchServiceUrl\":\"https:\/\/core.blockstack.org\/v1\/search?query={query}\",\"registerUrl\":\"https:\/\/core.blockstack.org\/v1\/names\",\"bitcoinAddressLookupUrl\":\"https:\/\/core.blockstack.org\/v1\/addresses\/bitcoin\/{address}\",\"zeroConfBalanceUrl\":\"https:\/\/core.blockstack.org\/v1\/wallet\/balance\/0\",\"insightUrl\":\"https:\/\/utxo.blockstack.org\/insight-api\/addr\/{address}\",\"btcBalanceUrl\":\"https:\/\/blockchain.info\/q\/addressbalance\/\",\"broadcastUrl\":\"https:\/\/utxo.blockstack.org\/insight-api\/tx\/send\",\"priceUrl\":\"https:\/\/core.blockstack.org\/v1\/prices\/names\/{name}\",\"networkFeeUrl\":\"https:\/\/bitcoinfees.21.co\/api\/v1\/fees\/recommended\",\"walletPaymentAddressUrl\":\"https:\/\/core.blockstack.org\/v1\/wallet\/payment_address\",\"pendingQueuesUrl\":\"https:\/\/core.blockstack.org\/v1\/blockchains\/bitcoin\/pending\",\"coreWalletWithdrawUrl\":\"https:\/\/core.blockstack.org\/v1\/wallet\/balance\",\"bitcoinAddressUrl\":\"https:\/\/explorer.blockstack.org\/address\/{identifier}\",\"ethereumAddressUrl\":\"https:\/\/tradeblock.com\/ethereum\/account\/{identifier}\",\"pgpKeyUrl\":\"https:\/\/pgp.mit.edu\/pks\/lookup?search={identifier}&op=vindex&fingerprint=on\",\"btcPriceUrl\":\"https:\/\/www.bitstamp.net\/api\/v2\/ticker\/btcusd\/?cors=1\",\"corePingUrl\":\"https:\/\/core.blockstack.org\/v1\/node\/ping\",\"zoneFileUrl\":\"https:\/\/core.blockstack.org\/v1\/names\/{name}\/zonefile\",\"nameTransferUrl\":\"https:\/\/core.blockstack.org\/v1\/names\/{name}\/owner\",\"subdomains\":{\"foo.id\":{\"registerUrl\":\"http:\/\/localhost:7103\/register\"},\"test-personal.id\":{\"registerUrl\":\"https:\/\/test-registrar.blockstack.org\/register\"},\"id.blockstack\":{\"registerUrl\":\"https:\/\/registrar.blockstack.org\/register\"}},\"browserServerUrl\":\"https:\/\/blockstack-browser-server.appartisan.com\",\"hostedDataLocation\":\"gaia-hub\",\"coreHost\":\"localhost\",\"corePort\":6270,\"coreAPIPassword\":\"PretendPasswordAPI\",\"logServerPort\":\"\",\"regTestMode\":false,\"storageConnected\":true,\"gaiaHubConfig\":{\"url_prefix\":\"https:\/\/gaia.blockstack.org\/hub\/\"},\"gaiaHubUrl\":\"https:\/\/hub.blockstack.org\",\"btcPrice\":\"1000.00\",\"distinctEventId\":\"063b145bc3f59dca962d585947f89d7a\",\"hasDisabledEventTracking\":false}},\"notifications\":[]}" -}; diff --git a/test-e2e/auth/sign-in.js b/test-e2e/auth/sign-in.js deleted file mode 100644 index 9a7e991cd..000000000 --- a/test-e2e/auth/sign-in.js +++ /dev/null @@ -1,228 +0,0 @@ -const { WebDriver, Builder, By, Key, until } = require('selenium-webdriver'); -const { expect } = require('chai'); -const createTestSuites = require('../utils/create-test-suites'); -const helpers = require('../utils/helpers'); -const canOpenProtocol = require('../utils/can-open-protocol'); -const sampleAccount = require('./sample-account'); -const createHelloBlockStackServer = require('../hello-blockstack-app/server'); - -const helloServerPort = 5790; - -// A promise that resolves to a running instance of the express server for the hello-blockstack webapp. -// Only gets instantiated when this test suite is ran, and should only be created once. -let helloServer; - -createTestSuites('login-to-hello-blockstack-app', ({driver, browserHostUrl, loopbackHost, browserName, browserStackEnabled}) => { - - before('spawn web server for hello-blockstack app', async () => { - // We only need to initialize this server once, so assign the promise object immediately, - // so that subsequent executions do not attempt to spawn a new server. - helloServer = helloServer || createHelloBlockStackServer(helloServerPort); - await helloServer; - }); - - before('check if supported environment', function () { - if (!browserStackEnabled && browserName === 'chrome' && canOpenProtocol()) { - console.log('Skipping for Chrome - the native app is installed on this machine and Selenium is incapable of progressing past the open prompt.'); - this.skip(); - } - }) - - step('load initial page', async () => { - await driver.get(browserHostUrl); - await driver.el(By.xpath('//*[contains(.,"Create your Blockstack ID")]')); - }); - - step('load app list', async () => { - await driver.waitForElementLocated(By.id('apps-loaded')); - }); - - step('fast account recovery via localStorage update', async () => { - // This test suite is for testing "sign in with Blockstack" on a 3rd party - // web app, and we don't care about or want to waste test execution time doing - // account restoration the long way. - // Directly write the sample account localStorage data as a quick and dirty way - // to restore the account into the blockstack browser session. - await driver.executeScript(` - window.localStorage.setItem("BLOCKSTACK_STATE_VERSION", "ignore"); - var authedReduxObj = JSON.parse(arguments[0]); - var localReduxObj = JSON.parse(window.localStorage.getItem("redux")); - var mergedReduxState = Object.assign({}, localReduxObj, authedReduxObj); - window.localStorage.setItem("redux", JSON.stringify(mergedReduxState)); - `, sampleAccount.LOCAL_STORAGE_DATA); - - // Wait a bit for localStorage writes since some browsers will not flush changes to disk if - // page is immediately navigated away. - await driver.sleep(100); - }); - - step('load page', async () => { - await driver.navigate().to(`http://${loopbackHost}:${helloServerPort}`); - }); - - step('set blockstack auth host', async () => { - await driver.executeScript(` - window.BLOCKSTACK_HOST = '${browserHostUrl}/auth'; - `); - }); - - step('click login button', async () => { - const windowHandle = await driver.getWindowHandle(); - await driver.click(By.css('#signin-button')); - await driver.sleep(1500); - if (await driver.elementExists(By.css('#signin-button'))) { - // This closes the "Open app?" dialog on Edge. - console.log('Performing window open & switch workaround for closing protocol handler dialog'); - await driver.executeScript(`window.open("about:config")`); - await driver.sleep(1000); - await driver.switchTo().window(windowHandle); - await driver.sleep(4000); - } - }); - - step('wait for auth page to load', async () => { - await driver.el(By.xpath('//div[contains(.,"Select an ID")]')); - }); - - step('click allow auth button', async () => { - await driver.click(By.xpath('//span[text()="test_e2e_recovery"]')); - }); - - step('ensure logged into hello-blockstack app', async () => { - await driver.el(By.xpath('//div[contains(.,"Hello, Alice")]')); - }); - - let userData; - step('validate blockstack user data', async () => { - userData = await driver.executeScript(`return blockstack.loadUserData()`); - expect(userData.appPrivateKey).to.have.lengthOf(64); - expect(userData.decentralizedID).to.equal("did:btc-addr:1NDsatzAEqrErxkB1osfJXouADgrHXuDs1"); - expect(userData.hubUrl).to.equal("https://hub.blockstack.org"); - expect(userData.username).to.equal("test_e2e_recovery.id.blockstack"); - expect(userData.profile.name).to.equal("Alice Devname"); - expect(userData.profile.api.gaiaHubUrl).to.equal("https://hub.blockstack.org"); - expect(userData.profile.api.gaiaHubConfig.url_prefix).to.equal("https://gaia.blockstack.org/hub/"); - }); - - step('validate blockstack.encryptContent(...) & blockstack.decryptContent(...) with account key', async () => { - const exampleData = "example data"; - const cipher = await driver.executeScript(`return blockstack.encryptContent(arguments[0])`, exampleData); - expect(JSON.parse(cipher)["wasString"]).to.be.true; - const decrypted = await driver.executeScript(`return blockstack.decryptContent(arguments[0])`, cipher); - expect(decrypted).to.equal(exampleData); - }); - - step('validate blockstack.encryptContent(...) & blockstack.decryptContent(...) with specified keys', async () => { - const publicKey = "0420a5c99852eae2e51d2638564cb4eb1066ac0126a534b25c31a9386b0d97c55abf77ba60dd029be0414d082a2acbc1477ebb6e028d37bbe16c354532e9de61dc"; - const privateKey = "80e626ad4bf501f58be7a1c4763a4c544bb83cc334ebee122321e8f30e41770f"; - const exampleData = "example data"; - const cipher = await driver.executeScript( - `return blockstack.encryptContent(arguments[0], arguments[1])`, - exampleData, { publicKey: publicKey }); - expect(JSON.parse(cipher)["wasString"]).to.be.true; - const decrypted = await driver.executeScript( - `return blockstack.decryptContent(arguments[0], arguments[1])`, - cipher, { privateKey: privateKey }); - expect(decrypted).to.equal(exampleData); - }); - - step('validate blockstack.getAppBucketUrl(...)', async () => { - const appBucketUrl = await driver.executePromise( - `blockstack.getAppBucketUrl(arguments[0], arguments[1])`, - userData.hubUrl, - userData.appPrivateKey); - expect(appBucketUrl).to.match(new RegExp("https://gaia.blockstack.org/hub/[a-zA-Z0-9]{34}/")); - }); - - let gaiaFileData; - - step('validate blockstack.putFile(...)', async () => { - gaiaFileData = helpers.getRandomString(20); - const putFileResult = await driver.executePromise( - `blockstack.putFile("/hello.txt", arguments[0])`, - gaiaFileData); - expect(putFileResult).to.match(new RegExp("https://gaia.blockstack.org/hub/[a-zA-Z0-9]{34}//hello.txt")); - }); - - step('validate blockstack.getFile(...)', async () => { - const getFileResult = await driver.executePromise(`blockstack.getFile("/hello.txt")`); - expect(getFileResult).to.equal(gaiaFileData); - }); - - step('validate blockstack.listFiles(...)', async () => { - const [error, result] = await driver.executeAsyncScript(` - var callback = arguments[arguments.length - 1]; - var files = []; - blockstack.listFiles(function(filename) { - files.push(filename); - return true; - }) - .then(result => callback([null, {files: files, count: result}])) - .catch(error => callback([error.toString(), null])); - `); - if (error) { - throw new Error(error); - } - expect(result.count).to.be.greaterThan(0); - expect(result.files).to.include('/hello.txt'); - }); - - step('validate blockstack.getUserAppFileUrl(...)', async () => { - const userAppFileUrl = await driver.executePromise( - `blockstack.getUserAppFileUrl(arguments[0], arguments[1], arguments[2])`, - 'public/1547742731687.json', - 'mattlittle_test1.id.blockstack', - 'https://app.graphitedocs.com'); - expect(userAppFileUrl).to.equal("https://gaia.blockstack.org/hub/18e3diVDsRfq2ckqS56wYw9mQhS4kxC15F/public/1547742731687.json"); - }); - - step('validate blockstack.getFile(...) with multi-player storage', async () => { - const getFileResult = await driver.executePromise( - `blockstack.getFile(arguments[0], arguments[1])`, - 'public/1547742731687.json', { - username: 'mattlittle_test1.id.blockstack', - app: 'https://app.graphitedocs.com', - decrypt: false - }); - // Sanity check on content.. - const resultJson = JSON.parse(getFileResult); - expect(resultJson["shared"]).to.equal("2/21/2019"); - }); - - step('validate blockstack.signUserOut(...)', async () => { - const redirectUrl = `http://${loopbackHost}:${helloServerPort}/?some=param`; - await driver.executeScript(`blockstack.signUserOut(arguments[0])`, redirectUrl); - await driver.sleep(50); - await driver.el(By.css('#signin-button')); - const windowLocation = await driver.getCurrentUrl(); - expect(windowLocation).to.equal(redirectUrl); - }); - - step('validate localStorage user data is been cleared', async () => { - const localStorageSession = await driver.executeScript(`return window.localStorage.getItem('blockstack-session')`); - if (localStorageSession) { - const sessionJson = JSON.parse(localStorageSession); - const userData = sessionJson['userData']; - expect(userData).to.not.exist; - } - else { - expect(localStorageSession).to.not.exist; - } - }); - -}); - - -after('dispose of hello-blockstack web server', async () => { - if (helloServer) { - const helloServerRef = helloServer; - helloServer = null; - const { server } = await helloServerRef; - await new Promise(resolve => server.close(error => { - if (error) { - console.error(`Error disposing of hello-blockstack web server: ${error}`); - } - resolve(); - })); - } -}); diff --git a/test-e2e/conf.js b/test-e2e/conf.js new file mode 100755 index 000000000..a962bd3e1 --- /dev/null +++ b/test-e2e/conf.js @@ -0,0 +1,203 @@ +const path = require('path'); +const url = require('url'); +const glob = require('glob'); +const protractor = require.resolve('protractor'); + +const nodeModules = protractor.substring(0, protractor.lastIndexOf('node_modules') + 'node_modules'.length); +const seleniumJar = glob.sync(`${nodeModules}/protractor/**/selenium-server-standalone-*.jar`).pop(); + +const helpers = require('./src/utils/helpers'); + +const BROWSERSTACK_LOOPBACK_HOST = 'bs-local.com'; +const BROWSERSTACK_HUB_URL = 'http://hub-cloud.browserstack.com/wd/hub'; +const browserStackEnvironments = require('./src/utils/browserstack-environments'); + +const config = { + params: { + browserHostUrl: '', + browserStack: { + enabled: false, + user: '', + key: '', + localEnabled: false, + localIdentifier: '', + hubUrl: '', + appium_version: '1.14.0' + }, + serveDirectory: '', + loopbackHost: 'localhost' + }, + // https://github.com/angular/protractor/blob/master/docs/timeouts.md + allScriptsTimeout: 60000, + getPageTimeout: 60000, + SELENIUM_PROMISE_MANAGER: false, + + // Load Serenity/JS + framework: 'custom', + frameworkPath: require.resolve('serenity-js'), + + specs: ['features/**/*.feature'], + + cucumberOpts: { + require: ['features/**/*.ts'], + format: 'pretty', + compiler: 'ts:ts-node/register' +}, + shardTestFiles: true, + maxSessions: 2, + commonCapabilities:{}, +}; + +/** + * Note: This config has to be loaded immediately and synchrMochaonously since the config values + * are used for setting up the env to run the test suites. + */ +(function initializeConfig() { + // Determine which browser host endpoint to run tests against. + const E2E_BROWSER_HOST = 'E2E_BROWSER_HOST'; + const PROD_HOST = 'https://browser.blockstack.org'; + config.params.browserHostUrl = process.env[E2E_BROWSER_HOST] || PROD_HOST; + if (!process.env[E2E_BROWSER_HOST]) { + console.warn(`WARNING: The browser host url was not set via the ${E2E_BROWSER_HOST} env var.. running tests against the production endpoint "${PROD_HOST}"`); + } else if (config.params.browserHostUrl.startsWith('http:') || config.params.browserHostUrl.startsWith('https:')) { + console.log(`Running e2e tests against endpoint ${config.params.browserHostUrl}`); + } else { + config.params.serveDirectory = path.resolve(config.params.browserHostUrl); + config.params.browserHostUrl = 'http://localhost:5757'; + console.log(`Local static web server will be started at ${config.params.browserHostUrl} for directory ${config.params.serveDirectory}`); + } + + // Check environment vars for BrowserStack usage settings. + const USE_BROWSERSTACK = 'USE_BROWSERSTACK'; + const BROWSERSTACK_AUTH = 'BROWSERSTACK_AUTH'; + config.params.browserStack.enabled = !helpers.isFalsy(process.env[USE_BROWSERSTACK]); + if (config.params.browserStack.enabled) { + config.params.browserStack.hubUrl = BROWSERSTACK_HUB_URL; + const browserstackAuth = process.env[BROWSERSTACK_AUTH]; + if (!browserstackAuth) { + const errMsg = `The BrowserStack auth must be set as environment variables. Use the format \`${BROWSERSTACK_AUTH}="user:key"\``; + console.error(errMsg); + throw new Error(errMsg); + } + // Auth string formatted as "user:key" + [config.params.browserStack.user, config.params.browserStack.key] = browserstackAuth.trim().split(/:(.+)/); + [config.browserstackUser, config.browserstackKey] = browserstackAuth.trim().split(/:(.+)/); + config.commonCapabilities = { + 'browserstack.user': config.params.browserStack.user, + 'browserstack.key': config.params.browserStack.key, + 'browserstack.console': 'verbose', + 'browserstack.debug': 'true' + }; + config.multiCapabilities = browserStackEnvironments; + } + + /** + * If the auth-browser host endpoint is set to localhost and BrowserStack testing is enabled + * then BrowserStack Local must be used. + * @see https://www.npmjs.com/package/browserstack-local + * @see https://www.browserstack.com/local-testing + */ + if (config.params.browserStack.enabled) { + const RANDOM_STRING = 'RANDOM_STRING'; + const parsedUrl = url.parse(config.params.browserHostUrl); + config.params.browserStack.localEnabled = ['localhost', '127.0.0.1'].includes(parsedUrl.hostname); + config.params.browserStack.localIdentifier = process.env[RANDOM_STRING]; + + /** + * Check if the host port is the expected port that is supported by BrowserStack Safari environments. + * @see https://www.browserstack.com/question/664 + */ + const expectedPort = '5757'; + if (config.params.browserStack.localEnabled && parsedUrl.port !== expectedPort) { + console.warn(`WARNING: BrowserStack Local is enabled but the host port is ${parsedUrl.port} rather than the expected port ${expectedPort}. ` + + `This may cause problems for BrowserStack Safari environments.. for more information see https://www.browserstack.com/question/664`); + } + + config.commonCapabilities = { + 'browserstack.console': 'verbose', + 'browserstack.debug': 'true' + }; + } + + /** + * If BrowserStack is enabled, then include their 'fast-selenium.js' script. + * @see https://www.browserstack.com/automate/node#add-on + * @see https://raw.githubusercontent.com/browserstack/fast-selenium-scripts/master/node/fast-selenium.js + */ + if (config.params.browserStack.enabled) { + require('./src/utils/fast-selenium'); + } + + /** + * If BrowserStack Local is enabled then the host url needs swapped from localhost to bs-local.com + * This required due to a technical limitation with BrowserStack's Safari environments. + * @see https://www.browserstack.com/question/759 + */ + if (config.params.browserStack.localEnabled) { + const parsedUrl = url.parse(config.params.browserHostUrl); + [parsedUrl.hostname, parsedUrl.host] = [BROWSERSTACK_LOOPBACK_HOST, undefined]; + config.params.browserHostUrl = url.format(parsedUrl); + config.params.loopbackHost = BROWSERSTACK_LOOPBACK_HOST; + + config.commonCapabilities = { + 'browserstack.console': 'verbose', + 'browserstack.local': 'true', + 'browserstack.debug': 'true', + 'browserstack.localIdentifier': config.params.browserStack.localIdentifier + }; + } + + if (!config.params.browserStack.enabled) { + const browsers = [ { + 'browserName': 'firefox' + }, { + 'browserName': 'chrome' + }]; + + if (process.platform === 'darwin') { + browsers.push({ + 'browserName': 'safari' + }); + } else if (process.platform === 'win32') { + browsers.push({ + 'browserName': 'edge' + }); + } + config.multiCapabilities = browsers; + config.seleniumServerJar = seleniumJar; + } + + if (config.params.browserStack.enabled) { + // Code to support common capabilities + config.multiCapabilities.forEach(function (caps) { + for (let i in config.commonCapabilities) caps[i] = caps[i] || config.commonCapabilities[i] + }); + } + + // Trim trailing url slash(es). + config.params.browserHostUrl = config.params.browserHostUrl.replace(/\/+$/, ''); + const CUCUMBER_TAG = 'CUCUMBER_TAG'; + if (process.env[E2E_BROWSER_HOST]) { + config.cucumberOpts.tags = process.env[CUCUMBER_TAG]; + } + return config; +})(); + + +// Prevent the error message +// "MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 exit listeners added. Use emitter.setMaxListeners() to increase limit" +// We don't care about this.. +// https://github.com/SeleniumHQ/selenium/issues/6812 +// https://github.com/nightwatchjs/nightwatch/issues/408 +process.setMaxListeners(0); +require('events').EventEmitter.defaultMaxListeners = 1000; + + +// +// afterLaunch(exitcode) { +// return new Promise(function (resolve) { +// reporter.afterLaunch(resolve.bind(this, exitcode)); +// }); +// }, + +module.exports = config; diff --git a/test-e2e/features/blockstack/blockstack.feature b/test-e2e/features/blockstack/blockstack.feature new file mode 100755 index 000000000..22ce228f1 --- /dev/null +++ b/test-e2e/features/blockstack/blockstack.feature @@ -0,0 +1,64 @@ +Feature: BlockStack + + @login + Scenario:login-to-hello-blockstack-app + Given load initial page + And load app list + Then fast account recovery via localStorage update + And load page + Then set blockstack auth host + And click login button +# And wait for auth page to load + Then click allow auth button + And ensure logged into hello-blockstack app + Then validate blockstack user data + Then validate blockstack encryptContent and blockstack decryptContent with account key + And validate blockstack encryptContent and blockstack decryptContent and with specified keys + Then validate blockstack getAppBucketUrl + And validate blockstack putFile + And validate blockstack getFile + Then validate blockstack listFiles + Then validate blockstack getUserAppFileUrl + And validate blockstack getFile with multi-player storage + Then validate blockstack signUserOut + And validate localStorage user data is been cleared + + @accountCreation + Scenario: Account Creation + Given load initial page + And set "test-registrar.blockstack.org" as API endpoint for ID registration + And load create new ID page + Then enter unique username + Then enter password + And wait for creating Blockstack ID spinner + Then enter email + And expect recovery email to fail + Then check username registration failed + And acknowledge saving recovery key phrase + Then wait for unlocking recovery key + And get secret recovery key phrase + Then perform recovery key phrase verification instructions + And load main page as authenticated user + + @magicRecovery + Scenario:account-recovery-via-magic-recovery-code + Given load initial page + And load sign in page + Then enter secret recovery key + Then enter blockstack password +# And wait for Loading spinner + Then enter blockstack email + And wait for Restoring your Blockstack ID + Then load main page for authenticated user + + @secretRecovery + Scenario:account-recovery-via-secret-key + Given load initial page + And load sign in page + Then enter blockstack secret recovery key + And create blockstack password + Then enter blockstack browser email + Then wait for Restoring your Blockstack ID + And load main page for authenticated user + + diff --git a/test-e2e/hello-blockstack-app/icon-192x192.png b/test-e2e/features/hello-blockstack-app/icon-192x192.png old mode 100644 new mode 100755 similarity index 100% rename from test-e2e/hello-blockstack-app/icon-192x192.png rename to test-e2e/features/hello-blockstack-app/icon-192x192.png diff --git a/test-e2e/hello-blockstack-app/index.html b/test-e2e/features/hello-blockstack-app/index.html old mode 100644 new mode 100755 similarity index 100% rename from test-e2e/hello-blockstack-app/index.html rename to test-e2e/features/hello-blockstack-app/index.html diff --git a/test-e2e/hello-blockstack-app/server.js b/test-e2e/features/hello-blockstack-app/server.js old mode 100644 new mode 100755 similarity index 87% rename from test-e2e/hello-blockstack-app/server.js rename to test-e2e/features/hello-blockstack-app/server.js index 98be2f992..3dd9c2a0b --- a/test-e2e/hello-blockstack-app/server.js +++ b/test-e2e/features/hello-blockstack-app/server.js @@ -1,23 +1,21 @@ const path = require('path'); const express = require('express'); const blockstackDistPath = require.resolve('blockstack/dist/blockstack'); +const fs = require('fs'); +const app = express(); - -async function startServer(port = 5000) { - - const app = express(); - +async function startServer(port = 5790) { function allowCrossDomain(req, res, next) { res.header('Access-Control-Allow-Origin', '*'); res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE'); res.header('Access-Control-Allow-Headers', 'Content-Type'); - next() + next(); } function getManifest(req, res) { // manifest.json must be constructed using the http request host - // due to technical limitations with BrowserStack's infrastructure - // which require re-routing `localhost` with another hostname. + // due to technical limitations with BrowserStack's infrastructure + // which require re-routing `localhost` with another hostname. const host = req.hostname; const manifest = { "name": "Hello, Blockstack", @@ -31,14 +29,11 @@ async function startServer(port = 5000) { }; res.json(manifest) } - - - app.use(allowCrossDomain); app.use('/', express.static(__dirname), express.static(path.dirname(blockstackDistPath))); app.use('/manifest.json', getManifest); - - const server = await new Promise((resolve, reject) => { + + const server = await new Promise((resolve, reject) => { const server = app.listen(port, error => { if (error) { console.error(`Error starting hello-blockstack server: ${error}`); @@ -56,5 +51,6 @@ async function startServer(port = 5000) { url: `http://localhost:${port}` } } +startServer(); -module.exports = startServer +module.exports = startServer; diff --git a/test-e2e/features/step_definitions/accountCreation.steps.ts b/test-e2e/features/step_definitions/accountCreation.steps.ts new file mode 100755 index 000000000..d58342622 --- /dev/null +++ b/test-e2e/features/step_definitions/accountCreation.steps.ts @@ -0,0 +1,173 @@ +const {Local: BrowserStackLocal} = require('browserstack-local'); +const browserstack = require("browserstack-local"); +import * as chai from 'chai'; +import * as chaiAsPromised from 'chai-as-promised'; +import {createServer, IncomingMessage, ServerResponse} from 'http'; +import {browser, By, element, utils} from 'protractor'; +import * as url from 'url'; +import {Utils} from '../../src/utils/Utils'; + +const {createServer: createHttpServer, Server: HttpServer} = require('http'); +const serveHandler = require('serve-handler'); + + +chai.use(chaiAsPromised); +const expect = chai.expect; + +module.exports = function myStepDefinitions() { +// selenium-webdriver docs: https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/webdriver.html + + let staticWebServer; + let randomUsername; + + this.Before(async () => { + await browser.waitForAngularEnabled(false); + }); + + this.After(async () => { + try { + await browser.manage().deleteAllCookies(); + } catch (e) { + console.log("Cookies are not deleted."); + } + await browser.get(browser.params.browserHostUrl); + //TODO: remove because parallel sessions can affect each other + await browser.executeScript('window.sessionStorage.clear();'); + await browser.executeScript('window.localStorage.clear();'); + + if (staticWebServer && staticWebServer.listening) { + console.log(`Stopping static web server`); + // Check if a local web server needs to be shutdown + if (staticWebServer && staticWebServer.listening) { + await new Promise((resolve) => { + staticWebServer.close((error) => { + if (error) { + console.error(`Error stopping local static web server: ${error}`); + } + resolve(); + }); + }); + } + } + }); + + this.Given(/^load initial page$/, async () => { + try { + await browser.get("https://google.com").then(function() { + console.log('Naviagate go temp page google.com'); + }, function(err) { + console.error('Error open google.com ' + err); + throw err; + }); + } + catch(err) { + console.log('Trying one more time after sleep 10 seconds ...'); + browser.sleep(10000) + await browser.get("https://google.com") + } + await browser.get(browser.params.browserHostUrl); + await Utils.waitForElementToDisplayed(element(By.xpath('//*[contains(.,"Create your Blockstack ID")]'))); + }); + + if (!process.env['TEST_PRODUCTION_REGISTRAR']) { + this.Given(/^set "([^"]*)" as API endpoint for ID registration$/, async (arg1) => { + await Utils.retry(async () => { + await browser.executeScript(`window.SUBDOMAIN_SUFFIX_OVERRIDE = "test-personal.id";`); + }, 10000, 200); + }); + } + + this.Given(/^load create new ID page$/, async () => { + await Utils.click(element(By.xpath('//div[text()="Create new ID"]'))); + }); + + this.Given(/^enter unique username$/, async () => { + /* tslint:disable:no-bitwise */ + randomUsername = `test_e2e_${Date.now() / 100000 | 0}_${Math.floor(Math.random() * (99999999999 - 1000000000)) + 1000000000}`; + /* tslint:disable:no-bitwise */ + await Utils.sendKeys(element(By.css('input[type="text"][name="username"]')), randomUsername); + await Utils.click(element(By.xpath('//button[contains(., "Check Availability")]'))); + await Utils.click(element(By.xpath('//button[contains(., "Continue")]'))); + }); + + this.Given(/^enter password$/, async () => { + const randomPassword = Math.random().toString(36).substr(2); + await Utils.sendKeys(element(By.css('input[type="password"][name="password"]')), randomPassword); + await Utils.sendKeys(element(By.css('input[type="password"][name="passwordConfirm"]')), randomPassword); + await Utils.click(element(By.xpath('//button[contains(., "Register ID")]'))); + }); + + this.Given(/^wait for creating Blockstack ID spinner$/, async () => { + // await Utils.waitForElement(element(By.xpath('//*[contains(text(), "Creating your Blockstack ID")]'))); + await browser.sleep(1999); + await Utils.waitForElementToDisappear(element(By.xpath('//*[contains(text(), "Creating your Blockstack ID")]'))); + await Utils.waitForElement(element(By.xpath('//*[contains(text(), "What is your email")]'))); + }); + + this.Given(/^enter email$/, async () => { + await Utils.sendKeys(element(By.css('input[type="email"][name="email"]')), `${randomUsername}@none.test`); + await Utils.click(element(By.xpath('//button[contains(., "Next")]'))); + }); + + this.Given(/^expect recovery email to fail$/, async () => { + await Utils.waitForElement(element(By.xpath('//*[contains(., "email failed")]'))); + }); + + this.Given(/^check username registration failed$/, async () => { + try { + await Utils.waitForElement(element(By.xpath('//*[text()="Username Registration Failed"]'))); + await Utils.click(element(By.xpath('//*[text()="Username Registration Failed"]/parent::div/following-sibling::div/descendant::span'))); + } catch (err) { + console.log(err) + } + }); + + this.Given(/^acknowledge saving recovery key phrase$/, async () => { + try { + await Utils.click(element(By.xpath('//div[text()="Secret Recovery Key"]/parent::div'))); + await Utils.waitForElement(element(By.xpath('//div[contains(.,"Save your Secret Recovery")]'))); + await Utils.click(element(By.xpath('//div[text()="Secret Recovery Key"]/parent::div'))); + } catch (err) { + console.log(err) + } + }); + this.Then(/^wait for unlocking recovery key$/, async () => { + try { + await Utils.waitForElement(element(By.xpath('//*[contains(text(), "Unlocking Recovery Key")]'))); + } catch (err) { + console.warn(`Error checking for "Unlocking Recovery Key" spinner: ${err}`); + } + await Utils.waitForElement(element(By.xpath('//*[text()="Your Secret Recovery Key"]/following-sibling::*')), + { timeout: 90000, poll: 200, driverWait: 90000 }); + }); + let keyWords; + + this.Given(/^get secret recovery key phrase$/, async () => { + try { + const elm = await Utils.waitForElement(element(By.xpath('//*[text()="Your Secret Recovery Key"]/following-sibling::*'))); + const keyEl = await elm.getText(); + keyWords = keyEl.trim().split(' '); + expect(keyWords).lengthOf(12, 'Recovery key phrase should be 12 words'); + await Utils.click(element(By.xpath('//div[text()="Continue"]/parent::div'))); + } catch (err) { + } + }); + + this.Given(/^perform recovery key phrase verification instructions$/, async () => { + const selectWordsEl = await browser.element(By.xpath('//*[contains(text(), "Select words #")]')); + const selectWords = await selectWordsEl.getText(); + // Parse the phrase word numbers to validate + /* tslint:disable-next-line: radix */ + const selectWordes = selectWords.match(/#([0-9]+)/g).map((s) => keyWords[parseInt(s.slice(1)) - 1]); + /* tslint:disable-next-line: radix */ + for (const selectWord of selectWordes) { + await Utils.click(element(By.xpath(`//div[span[text()="${selectWord}"]]`))); + } + + }); + + this.Then(/^load main page as authenticated user$/, async () => { + await Utils.click(element(By.xpath('//div[text()="Go to Blockstack"]'))); + await Utils.waitForElement(element(By.xpath('//*[text()="Top Apps"]'))); + }); +}; diff --git a/test-e2e/features/step_definitions/magic.steps.ts b/test-e2e/features/step_definitions/magic.steps.ts new file mode 100755 index 000000000..5df64121a --- /dev/null +++ b/test-e2e/features/step_definitions/magic.steps.ts @@ -0,0 +1,47 @@ +import {browser, By, element} from 'protractor'; +import {SampleAccount} from '../../src/utils/sample-account'; +import {Utils} from '../../src/utils/Utils'; + +module.exports = function MagicAccount() { + + this.Given(/^load sign in page$/, async () => { + await Utils.click(element(By.xpath('//a[contains(.,"Sign in with an existing ID")]'))); + }); + + this.Then(/^enter secret recovery key$/, async () => { + await Utils.sendKeys(element(By.css('textarea[name="recoveryKey"]')), SampleAccount.SECRET_RECOVERY_KEY); + await Utils.click(element(By.css('button[type="submit"]'))); + }); + + this.Then(/^enter blockstack password$/, async () => { + await Utils.sendKeys(element(By.css('input[name="password"]')), SampleAccount.PASSWORD); + await Utils.sendKeys(element(By.css('input[name="passwordConfirm"]')), SampleAccount.PASSWORD); + await Utils.click(element(By.css('button[type="submit"]'))); + }); + + this.Then(/^wait for Loading spinner$/, async () => { + await Utils.waitForElement(element(By.xpath('//*[contains(text(), "Loading")]'))); + await Utils.waitForElementToDisappear(element(By.xpath('//*[contains(text(), "Loading")]'))); + // wait for next page to load + await Utils.waitForElement(element(By.xpath('//*[contains(text(), "What is your email")]'))); + }); + + this.Given(/^enter blockstack email$/, async () => { + await Utils.sendKeys(element(By.css('input[name="email"]')), SampleAccount.EMAIL); + await Utils.click(element(By.css('button[type="submit"]'))); + }); + + this.Then(/^wait for Restoring your Blockstack ID$/, async () => { + // await Utils.waitForElement(element(By.xpath('//*[contains(text(), "Restoring your Blockstack ID")]'))); + await Utils.waitForElementToDisappear(element(By.xpath('//*[contains(text(), "Restoring your Blockstack ID")]'))); + // wait for next page to load + await Utils.waitForElement(element(By.xpath('//*[contains(.,"Go to Blockstack")]'))); + }); + + this.Then(/^load main page for authenticated user$/, async () => { + await Utils.click(element(By.css('div[class^="button__Label"]'))); + // await Utils.click(element(By.cssContainingText('div[class^="button__Label"]', 'Go to Blockstack'))); + await browser.sleep(2999); + await Utils.waitForElement(element(By.xpath('//*[text()="Top Apps"]'))); + }); +}; diff --git a/test-e2e/features/step_definitions/recoveraccount.steps.ts b/test-e2e/features/step_definitions/recoveraccount.steps.ts new file mode 100755 index 000000000..fbb7fe94b --- /dev/null +++ b/test-e2e/features/step_definitions/recoveraccount.steps.ts @@ -0,0 +1,23 @@ +import {browser, by, By, element, protractor, until} from 'protractor'; +import { SampleAccount } from '../../src/utils/sample-account'; +import {Utils} from '../../src/utils/Utils'; + +module.exports = function RecoverAccount() { + + this.Then(/^enter blockstack secret recovery key$/, async () => { + await Utils.sendKeys(element(By.css('textarea[name="recoveryKey"]')), SampleAccount.SECRET_RECOVERY_KEY); + await Utils.click(element(By.css('button[type="submit"]'))); + }); + + this.Then(/^create blockstack password$/, async () => { + await Utils.sendKeys(element(By.css('input[name="password"]')), SampleAccount.PASSWORD); + await Utils.sendKeys(element(By.css('input[name="passwordConfirm"]')), SampleAccount.PASSWORD); + await Utils.click(element(By.css('button[type="submit"]'))); + }); + + this.Then(/^enter blockstack browser email$/, async () => { + await Utils.sendKeys(element(By.css('input[name="email"]')), SampleAccount.EMAIL); + await Utils.click(element(By.css('button[type="submit"]'))); + }); + +}; diff --git a/test-e2e/features/step_definitions/sign.steps.ts b/test-e2e/features/step_definitions/sign.steps.ts new file mode 100755 index 000000000..1076fed99 --- /dev/null +++ b/test-e2e/features/step_definitions/sign.steps.ts @@ -0,0 +1,337 @@ +import * as chai from 'chai'; +// tslint:disable-next-line: prefer-const +import { browser, By, element, until } from 'protractor' +import {Canopenprotocol} from '../../src/utils/can-open-protocol'; +import {SampleAccount} from '../../src/utils/sample-account'; +import {Utils} from '../../src/utils/Utils'; + +const expect = chai.expect; +const error: any = ""; +const result: any = ""; +const loopbackHost = 'localhost'; +const helloServerPort = 5790; + +module.exports = function signIn() { + let helloServer; + let userData; + let cipher; + let gaiaFileData; + let getFileResult; + let localStorageSession; + let browserName; + + + + this.Before({tags: ["@login"]}, async () => { + const capabilities = await browser.getCapabilities(); + browserName = capabilities.get('browserName'); + console.log(`CURRENT browserName:${browserName}`); + // We only need to initialize this server once, so assign the promise object immediately, + // so that subsequent executions do not attempt to spawn a new server. + // try { + // helloServer = helloServer || createHelloBlockStackServer(helloServerPort); + // await helloServer; + // } catch (err) { + // console.log(err); + // } + + }); + + this.Before({tags: ["@login"]}, async () => { + const capabilities = await browser.getCapabilities(); + const browserName = capabilities.get('browserName'); + if (!browser.params.browserStack.enabled && browserName === 'chrome' && await Canopenprotocol.canOpenProtocol()) { + console.log('Skipping for Chrome - the native app is installed on this machine and Selenium is incapable of progressing past the open prompt.'); + this.skip(); + } + }); + + this.After({tags: ["@login"]}, async () => { + if (helloServer) { + const helloServerRef = helloServer; + helloServer = null; + const {server} = await helloServerRef; + await new Promise((resolve) => server.close(error => { + if (error) { + console.error(`Error disposing of hello-blockstack web server: ${error}`); + } + resolve(); + })); + } + }); + + async function login() { + const windowHandle = await browser.getWindowHandle(); + await Utils.waitForElement(element(By.css('#signin-button'))); + console.log("signin-button isDisplayed : " + await element(By.css('#signin-button')).isDisplayed()); + console.log("signin-button href: " + await element(By.css('#signin-button')).getAttribute("href")); + await browser.executeScript("document.getElementById('signin-button').click()"); + await browser.sleep(1500); + + const isSignInButtonDisplayed = await browser.element(By.css('#signin-button')).isPresent(); + if (isSignInButtonDisplayed) { + const capabilities = await browser.getCapabilities(); + const browserName = capabilities.get('browserName'); + if (browserName === 'MicrosoftEdge') { + // This closes the "Open app?" dialog on Edge. + console.log('Performing window open & switch workaround for closing protocol handler dialog'); + await browser.executeScript(`window.open("about:config")`); + await browser.sleep(1000); + await browser.switchTo().window(windowHandle); + await browser.sleep(4000); + } + } + } + + this.Then(/^load app list$/, async () => { + await browser.wait(until.elementLocated(By.id('apps-loaded')), 50000); + // await Utils.waitForElement(element(By.id('apps-loaded'))); + }); + + this.Then(/^fast account recovery via localStorage update$/, async () => { + await browser.executeScript(` + window.localStorage.setItem("BLOCKSTACK_STATE_VERSION", "ignore"); + var authedReduxObj = JSON.parse(arguments[0]); + var localReduxObj = JSON.parse(window.localStorage.getItem("redux")); + var mergedReduxState = Object.assign({}, localReduxObj, authedReduxObj); + window.localStorage.setItem("redux", JSON.stringify(mergedReduxState)); + `, SampleAccount.LOCAL_STORAGE_DATA); + + }); + this.Then(/^load page$/, async () => { + const capabilities = await browser.getCapabilities(); + const browserName = capabilities.get('browserName'); + console.log(`CURRENT browserName:${browserName}`); + if (browserName === 'safari') { + await browser.navigate().to(`http://${browser.params.loopbackHost}:${helloServerPort}`); + } + else { + await browser.navigate().to(`http://${loopbackHost}:${helloServerPort}`); + } + }); + + this.Given(/^set blockstack auth host$/, async () => { + await browser.executeScript(` + window.BLOCKSTACK_HOST = '${browser.params.browserHostUrl}/auth'; + `); + }); + + this.Then(/^click login button$/, async () => { + await login(); + }); + + this.Given(/^wait for auth page to load$/, async () => { + await browser.element(By.xpath('//div[contains(.,"Select an ID")]')); + }); + + this.Then(/^click allow auth button$/, async () => { + await Utils.waitForElementToDisplayed(element(By.xpath('//span[text()="test_e2e_recovery"]'))); + await Utils.click(element(By.xpath('//span[text()="test_e2e_recovery"]'))); + // await element(By.xpath('//span[text()="test_e2e_recovery"]')).click(); + }); + + this.Given(/^ensure logged into hello-blockstack app$/, async () => { + try { + await Utils.waitForElement(element(By.xpath('//div[contains(.,"Hello, Alice")]'))); + } + catch (e) { + //trying to login one more time + await browser.get(browser.params.browserHostUrl); + await Utils.waitForElementToDisplayed(element(By.xpath('//*[contains(.,"Create your Blockstack ID")]'))); + await browser.wait(until.elementLocated(By.id('apps-loaded')), 50000); + await browser.executeScript(` + window.localStorage.setItem("BLOCKSTACK_STATE_VERSION", "ignore"); + var authedReduxObj = JSON.parse(arguments[0]); + var localReduxObj = JSON.parse(window.localStorage.getItem("redux")); + var mergedReduxState = Object.assign({}, localReduxObj, authedReduxObj); + window.localStorage.setItem("redux", JSON.stringify(mergedReduxState)); + `, SampleAccount.LOCAL_STORAGE_DATA); + if (browserName === 'safari') { + await browser.navigate().to(`http://${browser.params.loopbackHost}:${helloServerPort}`); + } + else { + await browser.navigate().to(`http://${loopbackHost}:${helloServerPort}`); + } + await browser.executeScript(` + window.BLOCKSTACK_HOST = '${browser.params.browserHostUrl}/auth'; + `); + await login(); + await Utils.waitForElementToDisplayed(element(By.xpath('//span[text()="test_e2e_recovery"]'))); + await Utils.click(element(By.xpath('//span[text()="test_e2e_recovery"]'))); + await Utils.waitForElement(element(By.xpath('//div[contains(.,"Hello, Alice")]'))); + } + }); + + this.Then(/^validate blockstack user data$/, async () => { + userData = await browser.executeScript(`return blockstack.loadUserData()`); + await expect(userData.appPrivateKey).to.have.lengthOf(64); + await expect(userData.decentralizedID).to.equal("did:btc-addr:1NDsatzAEqrErxkB1osfJXouADgrHXuDs1"); + + await expect(userData.hubUrl).to.equal("https://hub.blockstack.org"); + await expect(userData.username).to.equal("test_e2e_recovery.id.blockstack"); + await expect(userData.profile.name).to.equal("Alice Devname"); + await expect(userData.profile.api.gaiaHubUrl).to.equal("https://hub.blockstack.org"); + await expect(userData.profile.api.gaiaHubConfig.url_prefix).to.equal("https://gaia.blockstack.org/hub/"); + + }); + const exampleData = "example data"; + this.Then(/^validate blockstack encryptContent and blockstack decryptContent with account key$/, async () => { + try { + cipher = await browser.executeScript(`return blockstack.encryptContent(arguments[0])`, exampleData); + // tslint:disable-next-line: no-unused-expression + await expect(JSON.parse(cipher)["wasString"]).to.be.true; + const decrypted = await browser.executeScript(`return blockstack.decryptContent(arguments[0])`, cipher); + await expect(decrypted).to.equal(exampleData); + } catch (err) { + console.log(err); + } + }); + + this.Then(/^validate blockstack encryptContent and blockstack decryptContent and with specified keys$/, async () => { + const publicKey = "0420a5c99852eae2e51d2638564cb4eb1066ac0126a534b25c31a9386b0d97c55abf77ba60dd029be0414d082a2acbc1477ebb6e028d37bbe16c354532e9de61dc"; + const privateKey = "80e626ad4bf501f58be7a1c4763a4c544bb83cc334ebee122321e8f30e41770f"; +// tslint:disable-next-line: no-shadowed-variable + const exampleData = "example data"; + cipher = await browser.executeScript( + `return blockstack.encryptContent(arguments[0], arguments[1])`, + exampleData, {publicKey}); + // tslint:disable-next-line: no-unused-expression + await expect(JSON.parse(cipher)["wasString"]).to.be.true; + const decrypted = await browser.executeScript( + `return blockstack.decryptContent(arguments[0], arguments[1])`, + cipher, {privateKey}); + await expect(decrypted).to.equal(exampleData); + }); + + this.Then(/^validate blockstack getAppBucketUrl$/, async () => { + if (browserName === 'safari') { + //do nothing due bug on IOS + //https://github.com/appium/appium/issues/13013 + //https://github.com/w3c/webdriver/pull/1362 + } + else { + const appBucketUrl = await Utils.executePromise( + `blockstack.getAppBucketUrl(arguments[0], arguments[1])`, + userData.hubUrl, + userData.appPrivateKey); + expect(appBucketUrl).to.match(new RegExp("https://gaia.blockstack.org/hub/[a-zA-Z0-9]{34}/")); + } + }); + + this.Then(/^validate blockstack putFile$/, async () => { + if (browserName === 'safari') { + //do nothing due bug on IOS + //https://github.com/appium/appium/issues/13013 + //https://github.com/w3c/webdriver/pull/1362 + } + else { + gaiaFileData = '4jvqclnb3sf'; + const putFileResult = await Utils.executePromise( + `blockstack.putFile("/hello.txt", arguments[0])`, + gaiaFileData); + expect(putFileResult).to.match(new RegExp("https://gaia.blockstack.org/hub/[a-zA-Z0-9]{34}//hello.txt")); + } + }); + + this.Then(/^validate blockstack getFile$/, async () => { + if (browserName === 'safari') { + //do nothing due bug on IOS + //https://github.com/appium/appium/issues/13013 + //https://github.com/w3c/webdriver/pull/1362 + } + else { + const getFileResult = await Utils.executePromise(`blockstack.getFile("/hello.txt")`); + chai.expect(getFileResult[0]).to.equal(gaiaFileData); + } + }); + + this.Then(/^validate blockstack listFiles$/, async () => { + if (browserName === 'safari') { + //do nothing due bug on IOS + //https://github.com/appium/appium/issues/13013 + //https://github.com/w3c/webdriver/pull/1362 + } + else { + const result = await browser.executeAsyncScript(` + var callback = arguments[arguments.length - 1]; + var files = []; + blockstack.listFiles(function(filename) { + files.push(filename); + return true; + }) + .then(result => callback([null, {files: files, count: result}])) + `); + expect(result[1].count).to.be.greaterThan(0); + expect(result[1].files).to.include('/hello.txt'); + } + }); + + this.Given(/^validate blockstack getUserAppFileUrl$/, async () => { + if (browserName === 'safari') { + //do nothing due bug on IOS + //https://github.com/appium/appium/issues/13013 + //https://github.com/w3c/webdriver/pull/1362 + } + else { + const pack = 'public/1547742731687.json'; + const block = 'mattlittle_test1.id.blockstack'; + const graphite = 'https://app.graphitedocs.com'; + const userAppFileUrl = await Utils.executePromise( + `blockstack.getUserAppFileUrl(arguments[0], arguments[1], arguments[2])`, + pack, + block, + graphite, + ); + expect(userAppFileUrl[0]).to.equal("https://gaia.blockstack.org/hub/18e3diVDsRfq2ckqS56wYw9mQhS4kxC15F/public/1547742731687.json"); + } + }); + + this.Then(/^validate blockstack.getFile with multi-player storage$/, async () => { + if (browserName === 'safari') { + //do nothing due bug on IOS + //https://github.com/appium/appium/issues/13013 + //https://github.com/w3c/webdriver/pull/1362 + } + else { + const blockstack = { + username: 'mattlittle_test1.id.blockstack', + app: 'https://app.graphitedocs.com', + decrypt: false, + }; + const publicjson = 'public/1547742731687.json'; + getFileResult = await Utils.executePromise( + `blockstack.getFile(arguments[0], arguments[1])`, + publicjson, + blockstack, + ); + // Sanity check on content.. + const resultJson = JSON.parse(getFileResult); + expect(resultJson["shared"]).to.equal("2/21/2019"); + } + }); + + this.Then(/^validate blockstack.signUserOut$/, async () => { + const redirectUrl = `http://${browser.params.loopbackHost}:${helloServerPort}/?some=param`; + await browser.executeScript(`blockstack.signUserOut(arguments[0])`, redirectUrl); + await browser.sleep(50); + await Utils.waitForElement(element(By.css('#signin-button'))); + const windowLocation = await browser.getCurrentUrl(); + expect(windowLocation).to.equal(redirectUrl); + }); + + const newLocal = `return window.localStorage.getItem('blockstack-session')`; + this.Then(/^validate localStorage user data is been cleared$/, async () => { + localStorageSession = await browser.executeScript(newLocal); + if (localStorageSession) { + const sessionJson = JSON.parse(localStorageSession); + // tslint:disable-next-line: no-shadowed-variable + const userData = sessionJson['userData']; + // tslint:disable-next-line: no-unused-expression + await expect(userData).to.not.exist; + } else { + // tslint:disable-next-line: no-unused-expression + await expect(localStorageSession).to.not.exist; + } + }); +}; + diff --git a/test-e2e/features/support/world.ts b/test-e2e/features/support/world.ts new file mode 100755 index 000000000..d251ca041 --- /dev/null +++ b/test-e2e/features/support/world.ts @@ -0,0 +1,3 @@ +export = function() { + this.setDefaultTimeout(200 * 1000); +}; diff --git a/test-e2e/mocha.opts b/test-e2e/mocha.opts deleted file mode 100644 index 9eeac2800..000000000 --- a/test-e2e/mocha.opts +++ /dev/null @@ -1,17 +0,0 @@ ---require mocha-steps ---timeout 180000 ---full-trace ---recursive ---sort ---exclude **/run-parallel.js ---exclude **/hello-blockstack-app/** -# --fgrep firefox -# --fgrep safari -# --fgrep Win10-Chrome -# --fgrep Android -# --fgrep iOS -# --grep account\srecovery.*firefox -# --grep secret\skey.*iOS-12-iPhone-XS -# --grep magic\srecovery -# --grep secret\skey -# --grep account\srecovery \ No newline at end of file diff --git a/test-e2e/package-lock.json b/test-e2e/package-lock.json new file mode 100755 index 000000000..2c64dce9b --- /dev/null +++ b/test-e2e/package-lock.json @@ -0,0 +1,4653 @@ +{ + "name": "blockstack-sernityjs-test-e2e", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/code-frame": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", + "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", + "dev": true, + "requires": { + "@babel/highlight": "^7.0.0" + } + }, + "@babel/highlight": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz", + "integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + } + }, + "@serenity-js/core": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@serenity-js/core/-/core-1.6.0.tgz", + "integrity": "sha1-7dzJh6U8pG82pjmJjOMqMkHi++k=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "lodash": "4.17.4", + "mkdirp": "0.5.1", + "moment": "2.18.1", + "stacktrace-js": "2.0.0", + "ts-md5": "1.2.0" + }, + "dependencies": { + "error-stack-parser": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.0.2.tgz", + "integrity": "sha512-E1fPutRDdIj/hohG0UpT5mayXNCxXP9d+snxFsPU9X0XgccOumKraa3juDMwTUyi7+Bu5+mCGagjg4IYeNbOdw==", + "dev": true, + "requires": { + "stackframe": "^1.0.4" + } + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true + }, + "lodash": { + "version": "4.17.4", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=", + "dev": true + }, + "source-map": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=", + "dev": true + }, + "stack-generator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/stack-generator/-/stack-generator-2.0.3.tgz", + "integrity": "sha512-kdzGoqrnqsMxOEuXsXyQTmvWXZmG0f3Ql2GDx5NtmZs59sT2Bt9Vdyq0XdtxUi58q/+nxtbF9KOQ9HkV1QznGg==", + "dev": true, + "requires": { + "stackframe": "^1.0.4" + } + }, + "stackframe": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.0.4.tgz", + "integrity": "sha512-to7oADIniaYwS3MhtCa/sQhrxidCCQiF/qp4/m5iN3ipf0Y7Xlri0f6eG29r08aL7JYl8n32AF3Q5GYBZ7K8vw==", + "dev": true + }, + "stacktrace-gps": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/stacktrace-gps/-/stacktrace-gps-3.0.2.tgz", + "integrity": "sha512-9o+nWhiz5wFnrB3hBHs2PTyYrS60M1vvpSzHxwxnIbtY2q9Nt51hZvhrG1+2AxD374ecwyS+IUwfkHRE/2zuGg==", + "dev": true, + "requires": { + "source-map": "0.5.6", + "stackframe": "^1.0.4" + } + }, + "stacktrace-js": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/stacktrace-js/-/stacktrace-js-2.0.0.tgz", + "integrity": "sha1-d2ymRqlbxsayuQd2U2p/xyxt21g=", + "dev": true, + "requires": { + "error-stack-parser": "^2.0.1", + "stack-generator": "^2.0.1", + "stacktrace-gps": "^3.0.1" + } + } + } + }, + "@types/body-parser": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.17.0.tgz", + "integrity": "sha512-a2+YeUjPkztKJu5aIF2yArYFQQp8d51wZ7DavSHjFuY1mqVgidGyzEQ41JIVNy82fXj8yPgy2vJmfIywgESW6w==", + "dev": true, + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "@types/chai": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.1.7.tgz", + "integrity": "sha512-2Y8uPt0/jwjhQ6EiluT0XCri1Dbplr0ZxfFXUz+ye13gaqE8u5gL5ppao1JrUYr9cIip5S6MvQzBS7Kke7U9VA==", + "dev": true + }, + "@types/chai-as-promised": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@types/chai-as-promised/-/chai-as-promised-7.1.0.tgz", + "integrity": "sha512-MFiW54UOSt+f2bRw8J7LgQeIvE/9b4oGvwU7XW30S9QGAiHGnU/fmiOprsyMkdmH2rl8xSPc0/yrQw8juXU6bQ==", + "dev": true, + "requires": { + "@types/chai": "*" + } + }, + "@types/connect": { + "version": "3.4.32", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.32.tgz", + "integrity": "sha512-4r8qa0quOvh7lGD0pre62CAb1oni1OO6ecJLGCezTmhQ8Fz50Arx9RUszryR8KlgK6avuSXvviL6yWyViQABOg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/core-js": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@types/core-js/-/core-js-2.5.2.tgz", + "integrity": "sha512-+NPqjXgyA02xTHKJDeDca9u8Zr42ts6jhdND4C3PrPeQ35RJa0dmfAedXW7a9K4N1QcBbuWI1nSfGK4r1eVFCQ==", + "dev": true + }, + "@types/cucumber": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/@types/cucumber/-/cucumber-4.0.7.tgz", + "integrity": "sha512-uYvDSl+JV1fT1U9EIFXlnb7DjZdRNYZHQFppQf9aYAe71joppErWenznupwZUj9zuo+SAFYNoongarQpNOb+pQ==", + "dev": true + }, + "@types/express": { + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.0.tgz", + "integrity": "sha512-CjaMu57cjgjuZbh9DpkloeGxV45CnMGlVd+XpG7Gm9QgVrd7KFq+X4HY0vM+2v0bczS48Wg7bvnMY5TN+Xmcfw==", + "dev": true, + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "*", + "@types/serve-static": "*" + } + }, + "@types/express-serve-static-core": { + "version": "4.16.7", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.16.7.tgz", + "integrity": "sha512-847KvL8Q1y3TtFLRTXcVakErLJQgdpFSaq+k043xefz9raEf0C7HalpSY7OW5PyjCnY8P7bPW5t/Co9qqp+USg==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/range-parser": "*" + } + }, + "@types/mime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.1.tgz", + "integrity": "sha512-FwI9gX75FgVBJ7ywgnq/P7tw+/o1GUbtP0KzbtusLigAOgIgNISRK0ZPl4qertvXSIE8YbsVJueQ90cDt9YYyw==", + "dev": true + }, + "@types/mocha": { + "version": "5.2.7", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz", + "integrity": "sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==", + "dev": true + }, + "@types/node": { + "version": "8.10.51", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.51.tgz", + "integrity": "sha512-cArrlJp3Yv6IyFT/DYe+rlO8o3SIHraALbBW/+CcCYW/a9QucpLI+n2p4sRxAvl2O35TiecpX2heSZtJjvEO+Q==", + "dev": true + }, + "@types/q": { + "version": "0.0.32", + "resolved": "https://registry.npmjs.org/@types/q/-/q-0.0.32.tgz", + "integrity": "sha1-vShOV8hPEyXacCur/IKlMoGQwMU=", + "dev": true + }, + "@types/range-parser": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", + "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==", + "dev": true + }, + "@types/selenium-webdriver": { + "version": "3.0.16", + "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-3.0.16.tgz", + "integrity": "sha512-lMC2G0ItF2xv4UCiwbJGbnJlIuUixHrioOhNGHSCsYCJ8l4t9hMCUimCytvFv7qy6AfSzRxhRHoGa+UqaqwyeA==", + "dev": true + }, + "@types/serve-static": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha512-/BZ4QRLpH/bNYgZgwhKEh+5AsboDBcUdlBYgzoLX0fpj3Y2gp6EApyOlM3bK53wQS/OE1SrdSYBAbux2D1528Q==", + "dev": true, + "requires": { + "@types/express-serve-static-core": "*", + "@types/mime": "*" + } + }, + "address": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/address/-/address-1.1.0.tgz", + "integrity": "sha512-4diPfzWbLEIElVG4AnqP+00SULlPzNuyJFNnmMrLgyaxG6tZXJ1sn7mjBu4fHrJE+Yp/jgylOweJn2xsLMFggQ==", + "dev": true + }, + "adm-zip": { + "version": "0.4.13", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.13.tgz", + "integrity": "sha512-fERNJX8sOXfel6qCBCMPvZLzENBEhZTzKqg6vrOW5pvoEaQuJhRU4ndTAh6lHOxn1I6jnz2NHra56ZODM751uw==", + "dev": true + }, + "agent-base": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", + "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", + "dev": true, + "requires": { + "es6-promisify": "^5.0.0" + } + }, + "align-text": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", + "dev": true, + "requires": { + "kind-of": "^3.0.2", + "longest": "^1.0.1", + "repeat-string": "^1.5.2" + } + }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", + "dev": true + }, + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", + "dev": true + }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "^1.0.1" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + }, + "assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "autoprefixer": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-3.1.0.tgz", + "integrity": "sha1-tQ5syIpUP/gQGN+rBGfqdXCnoXI=", + "dev": true, + "requires": { + "autoprefixer-core": "~3.1.0", + "fs-extra": "~0.11.1", + "postcss": "~2.2.4" + }, + "dependencies": { + "fs-extra": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.11.1.tgz", + "integrity": "sha1-3xBPlMyEHu+Pr+KkRsiPXTW7Lnk=", + "dev": true, + "requires": { + "jsonfile": "^2.0.0", + "mkdirp": "^0.5.0", + "ncp": "^0.6.0", + "rimraf": "^2.2.8" + } + }, + "jsonfile": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + } + } + }, + "autoprefixer-core": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/autoprefixer-core/-/autoprefixer-core-3.1.2.tgz", + "integrity": "sha1-reXOni2dcbt//DHWlvpeh66+tjQ=", + "dev": true, + "requires": { + "caniuse-db": "^1.0.30000006", + "postcss": "~2.2.5" + } + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true + }, + "aws4": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "dev": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "blocking-proxy": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/blocking-proxy/-/blocking-proxy-1.0.1.tgz", + "integrity": "sha512-KE8NFMZr3mN2E0HcvCgRtX7DjhiIQrwle+nSVJVC/yqFb9+xznHl2ZcoBp2L9qzkI4t4cBFJ1efXF8Dwi132RA==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, + "boom": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/boom/-/boom-0.4.2.tgz", + "integrity": "sha1-emNune1O/O+xnO9JR6PGffrukRs=", + "dev": true, + "optional": true, + "requires": { + "hoek": "0.9.x" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "browser-stdout": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", + "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=", + "dev": true + }, + "browserstack": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/browserstack/-/browserstack-1.5.2.tgz", + "integrity": "sha512-+6AFt9HzhKykcPF79W6yjEUJcdvZOV0lIXdkORXMJftGrDl0OKWqRF4GHqpDNkxiceDT/uB7Fb/aDwktvXX7dg==", + "dev": true, + "requires": { + "https-proxy-agent": "^2.2.1" + } + }, + "browserstack-local": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/browserstack-local/-/browserstack-local-1.4.2.tgz", + "integrity": "sha512-fRaynjF0MvtyyfPRy2NFnVwxLyNtD28K/v9xRsIjUVf7xLc80NIm7Nfr3KXlFmWizhG91PL/UAOXlHkoxQjaNw==", + "dev": true, + "requires": { + "https-proxy-agent": "^2.2.1", + "is-running": "^2.0.0", + "ps-tree": "=1.1.1", + "temp-fs": "^0.9.9" + } + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "dev": true + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" + }, + "camel-case": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", + "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=", + "dev": true, + "requires": { + "no-case": "^2.2.0", + "upper-case": "^1.1.1" + } + }, + "camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "dev": true, + "requires": { + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" + }, + "dependencies": { + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "dev": true + } + } + }, + "caniuse-db": { + "version": "1.0.30000984", + "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000984.tgz", + "integrity": "sha512-1tismk25It1v7bWgRHkHxITa7ySDXVQCwb49iKbn/HeDBTEKOgEqKkJT2Xv5rJSneDqdQRqFvYrzvw5WulLjfQ==", + "dev": true + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, + "center-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", + "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", + "dev": true, + "requires": { + "align-text": "^0.1.3", + "lazy-cache": "^1.0.3" + } + }, + "chai": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", + "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", + "dev": true, + "requires": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^3.0.1", + "get-func-name": "^2.0.0", + "pathval": "^1.1.0", + "type-detect": "^4.0.5" + } + }, + "chai-as-promised": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.1.tgz", + "integrity": "sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==", + "dev": true, + "requires": { + "check-error": "^1.0.2" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "change-case": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/change-case/-/change-case-2.1.6.tgz", + "integrity": "sha1-UUryBRMVimj+fwDf9MMy1sKY0vk=", + "dev": true, + "requires": { + "camel-case": "^1.0.0", + "constant-case": "^1.0.0", + "dot-case": "^1.0.0", + "is-lower-case": "^1.0.0", + "is-upper-case": "^1.0.0", + "lower-case": "^1.0.0", + "param-case": "^1.0.0", + "pascal-case": "^1.0.0", + "path-case": "^1.0.0", + "sentence-case": "^1.0.0", + "snake-case": "^1.0.0", + "swap-case": "^1.0.0", + "title-case": "^1.0.0", + "upper-case": "^1.0.0", + "upper-case-first": "^1.0.0" + }, + "dependencies": { + "camel-case": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-1.2.2.tgz", + "integrity": "sha1-Gsp8TRlTWaLOmVV5NDPG5VQlEfI=", + "dev": true, + "requires": { + "sentence-case": "^1.1.1", + "upper-case": "^1.1.1" + } + } + } + }, + "check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", + "dev": true + }, + "ci-info": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.1.3.tgz", + "integrity": "sha512-SK/846h/Rcy8q9Z9CAwGBLfCJ6EkjJWdpelWDufQpqVDYq2Wnnv8zlSO6AMQap02jvhVruKKpEtQOufo3pFhLg==", + "dev": true + }, + "clean-css": { + "version": "2.2.23", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-2.2.23.tgz", + "integrity": "sha1-BZC1R4tRbEkD7cLYm9P9vdKGMow=", + "dev": true, + "requires": { + "commander": "2.2.x" + }, + "dependencies": { + "commander": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.2.0.tgz", + "integrity": "sha1-F1rUuTF/P/YV8gHB5XIk9Vo+kd8=", + "dev": true + } + } + }, + "cli": { + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/cli/-/cli-0.6.6.tgz", + "integrity": "sha1-Aq1Eo4Cr8nraxebwzdewQ9dMU+M=", + "dev": true, + "requires": { + "exit": "0.1.2", + "glob": "~ 3.2.1" + }, + "dependencies": { + "glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", + "integrity": "sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0=", + "dev": true, + "requires": { + "inherits": "2", + "minimatch": "0.3" + } + }, + "minimatch": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", + "integrity": "sha1-J12O2qxPG7MyZHIInnlJyDlGmd0=", + "dev": true, + "requires": { + "lru-cache": "2", + "sigmund": "~1.0.0" + } + } + } + }, + "cli-table": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.1.tgz", + "integrity": "sha1-9TsFJmqLGguTSz0IIebi3FkUriM=", + "dev": true, + "requires": { + "colors": "1.0.3" + }, + "dependencies": { + "colors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", + "dev": true + } + } + }, + "cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "dev": true, + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "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.3.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.3.tgz", + "integrity": "sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg==", + "dev": true + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", + "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "constant-case": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/constant-case/-/constant-case-1.1.2.tgz", + "integrity": "sha1-jsLKW6ND4Aqjjb9OIA/VrJB+/WM=", + "dev": true, + "requires": { + "snake-case": "^1.1.0", + "upper-case": "^1.1.1" + } + }, + "content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" + }, + "copy-paste": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/copy-paste/-/copy-paste-1.3.0.tgz", + "integrity": "sha1-p+bEocKP3t8rCB5yuX3y75X0ce0=", + "dev": true, + "requires": { + "iconv-lite": "^0.4.8", + "sync-exec": "~0.6.x" + } + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "cryptiles": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-0.2.2.tgz", + "integrity": "sha1-7ZH/HxetE9N0gohZT4pIoNJvMlw=", + "dev": true, + "optional": true, + "requires": { + "boom": "0.4.x" + } + }, + "ctype": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/ctype/-/ctype-0.5.3.tgz", + "integrity": "sha1-gsGMJGH3QRTvFsE1IkrQuRRMoS8=", + "dev": true, + "optional": true + }, + "cucumber": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/cucumber/-/cucumber-1.3.3.tgz", + "integrity": "sha1-Za+2Xy+T9y2teN8qterPFGCf7C8=", + "dev": true, + "requires": { + "camel-case": "^3.0.0", + "cli-table": "^0.3.1", + "co": "^4.6.0", + "colors": "^1.1.2", + "commander": "^2.9.0", + "duration": "^0.2.0", + "figures": "1.7.0", + "gherkin": "^4.1.0", + "glob": "^7.0.0", + "is-generator": "^1.0.2", + "lodash": "^4.0.0", + "stack-chain": "^1.3.5", + "stacktrace-js": "^1.3.0" + } + }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "dev": true, + "requires": { + "array-find-index": "^1.0.1" + } + }, + "cycle": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", + "integrity": "sha1-IegLK+hYD5i0aPN5QwZisEbDStI=", + "dev": true + }, + "cypress-serenity-reporter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cypress-serenity-reporter/-/cypress-serenity-reporter-1.0.1.tgz", + "integrity": "sha512-tAcKfZ50LOH7VgKsXtrZx1GlUcl3AbuDhPmOGAIzzNcfYJABl7bEUPMDdV5ySX+Zmo5UeArI3y8EKPFLadzc2g==", + "dev": true, + "requires": { + "@serenity-js/core": "^1.5.4", + "mocha": "^4.0.1" + }, + "dependencies": { + "commander": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", + "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", + "dev": true + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "mocha": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-4.1.0.tgz", + "integrity": "sha512-0RVnjg1HJsXY2YFDoTNzcc1NKhYuXKRrBAG2gDygmJJA136Cs2QlRliZG1mA0ap7cuaT30mw16luAeln+4RiNA==", + "dev": true, + "requires": { + "browser-stdout": "1.3.0", + "commander": "2.11.0", + "debug": "3.1.0", + "diff": "3.3.1", + "escape-string-regexp": "1.0.5", + "glob": "7.1.2", + "growl": "1.10.3", + "he": "1.1.1", + "mkdirp": "0.5.1", + "supports-color": "4.4.0" + } + } + } + }, + "d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "dev": true, + "requires": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "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" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "deep-eql": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "dev": true, + "requires": { + "type-detect": "^4.0.0" + } + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "del": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", + "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", + "dev": true, + "requires": { + "globby": "^5.0.0", + "is-path-cwd": "^1.0.0", + "is-path-in-cwd": "^1.0.0", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "rimraf": "^2.2.8" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "detect-port": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-1.3.0.tgz", + "integrity": "sha512-E+B1gzkl2gqxt1IhUzwjrxBKRqx1UzC3WLONHinn8S3T6lwV/agVCyitiFOsGJ/eYuEUBvD71MZHy3Pv1G9doQ==", + "dev": true, + "requires": { + "address": "^1.0.1", + "debug": "^2.6.0" + } + }, + "diff": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.3.1.tgz", + "integrity": "sha512-MKPHZDMB0o6yHyDryUOScqZibp914ksXwAMYMTHj6KO8UeKsRYNJD3oNCKjTqZon+V488P7N/HzXF8t7ZR95ww==", + "dev": true + }, + "dot-case": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-1.1.2.tgz", + "integrity": "sha1-HnOCaQDeKNbeVIC8HeMdCEKwa+w=", + "dev": true, + "requires": { + "sentence-case": "^1.1.2" + } + }, + "duplexer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", + "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", + "dev": true + }, + "duration": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/duration/-/duration-0.2.2.tgz", + "integrity": "sha512-06kgtea+bGreF5eKYgI/36A6pLXggY7oR4p1pq4SmdFBn1ReOL5D8RhG64VrqfTTKNucqqtBAwEj8aB88mcqrg==", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "~0.10.46" + } + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dev": true, + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "ejs2": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ejs2/-/ejs2-2.1.0.tgz", + "integrity": "sha1-9jBb6oAxaQDuqSjo4haFBM8Gwq8=", + "dev": true, + "requires": { + "debug": "~0.7.2" + }, + "dependencies": { + "debug": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-0.7.4.tgz", + "integrity": "sha1-BuHqgILCyxTjmAbiLi9vdX+Srzk=", + "dev": true + } + } + }, + "encoding": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", + "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", + "dev": true, + "requires": { + "iconv-lite": "~0.4.13" + } + }, + "end-of-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "error-stack-parser": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-1.3.6.tgz", + "integrity": "sha1-4Oc7k+QXE40c18C3RrGkoUhUwpI=", + "dev": true, + "requires": { + "stackframe": "^0.3.1" + } + }, + "es-abstract": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", + "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.0", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "is-callable": "^1.1.4", + "is-regex": "^1.0.4", + "object-keys": "^1.0.12" + } + }, + "es-to-primitive": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", + "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "es5-ext": { + "version": "0.10.50", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.50.tgz", + "integrity": "sha512-KMzZTPBkeQV/JcSQhI5/z6d9VWJ3EnQ194USTUwIYZ2ZbpN8+SGXQKt1h68EX44+qt+Fzr8DO17vnxrw7c3agw==", + "dev": true, + "requires": { + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.1", + "next-tick": "^1.0.0" + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", + "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.0.3" + } + }, + "es6-symbol": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", + "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, + "event-stream": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", + "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=", + "dev": true, + "requires": { + "duplexer": "~0.1.1", + "from": "~0", + "map-stream": "~0.1.0", + "pause-stream": "0.0.11", + "split": "0.3", + "stream-combiner": "~0.0.4", + "through": "~2.3.1" + } + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true + }, + "eyes": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", + "integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=", + "dev": true + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", + "dev": true + }, + "fast-url-parser": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/fast-url-parser/-/fast-url-parser-1.1.3.tgz", + "integrity": "sha1-9K8+qfNNiicc9YrSs3WfQx8LMY0=", + "requires": { + "punycode": "^1.3.2" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + } + } + }, + "figures": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", + "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5", + "object-assign": "^4.1.0" + } + }, + "file-type": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", + "integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek=", + "dev": true + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true + }, + "from": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", + "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=", + "dev": true + }, + "fs-tools": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/fs-tools/-/fs-tools-0.2.11.tgz", + "integrity": "sha1-ZKYzkRn42mh75NH9vaN+4PiOZEw=", + "dev": true, + "requires": { + "async": "~ 0.2.9", + "lodash": "~ 2.4.1" + }, + "dependencies": { + "async": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=", + "dev": true + }, + "lodash": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", + "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=", + "dev": true + } + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", + "dev": true + }, + "get-stdin": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz", + "integrity": "sha1-Ei4WFZHiH/TFJTAwVpPyDmOTo5g=", + "dev": true + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "gherkin": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/gherkin/-/gherkin-4.1.3.tgz", + "integrity": "sha1-EWh9uTl235djMSWmsiKKGkv9+iQ=", + "dev": true + }, + "glob": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "globby": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", + "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "arrify": "^1.0.0", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "graceful-fs": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.0.tgz", + "integrity": "sha512-jpSvDPV4Cq/bgtpndIWbI5hmYxhQGHPC4d4cqBPb4DLniCfhJokdXhwhaDuLBGLQdvvRum/UiX6ECVIPvDXqdg==", + "dev": true + }, + "growl": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz", + "integrity": "sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q==", + "dev": true + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true + }, + "har-validator": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "dev": true, + "requires": { + "ajv": "^6.5.5", + "har-schema": "^2.0.0" + }, + "dependencies": { + "ajv": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", + "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", + "dev": true, + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + } + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + } + } + }, + "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-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", + "dev": true + }, + "hawk": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-1.1.1.tgz", + "integrity": "sha1-h81JH5tG5OKurKM1QWdmiF0tHtk=", + "dev": true, + "optional": true, + "requires": { + "boom": "0.4.x", + "cryptiles": "0.2.x", + "hoek": "0.9.x", + "sntp": "0.2.x" + } + }, + "he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "dev": true + }, + "highlight.js": { + "version": "8.9.1", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-8.9.1.tgz", + "integrity": "sha1-uKnFSTISqTkvAiK2SclhFJfr+4g=", + "dev": true + }, + "hike": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/hike/-/hike-0.1.4.tgz", + "integrity": "sha1-06/Q4JIHnHsLLjw38uLJTqLGb14=", + "dev": true, + "requires": { + "lodash": "~ 2.4.1" + }, + "dependencies": { + "lodash": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", + "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=", + "dev": true + } + } + }, + "hoek": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-0.9.1.tgz", + "integrity": "sha1-PTIkYrrfB3Fup+uFuviAec3c5QU=", + "dev": true, + "optional": true + }, + "homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "dev": true, + "requires": { + "parse-passwd": "^1.0.0" + } + }, + "hosted-git-info": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", + "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", + "dev": true + }, + "html-minifier": { + "version": "0.6.9", + "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-0.6.9.tgz", + "integrity": "sha1-UQXcI29efhqLplHUq5gThvx6vlM=", + "dev": true, + "requires": { + "change-case": "2.1.x", + "clean-css": "2.2.x", + "cli": "0.6.x", + "relateurl": "0.2.x", + "uglify-js": "2.4.x" + }, + "dependencies": { + "async": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=", + "dev": true + }, + "camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", + "dev": true + }, + "source-map": { + "version": "0.1.34", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.34.tgz", + "integrity": "sha1-p8/omux7FoLDsZjQrPtH19CQVms=", + "dev": true, + "requires": { + "amdefine": ">=0.0.4" + } + }, + "uglify-js": { + "version": "2.4.24", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.4.24.tgz", + "integrity": "sha1-+tV1XB4Vd2WLsG/5q25UjJW+vW4=", + "dev": true, + "requires": { + "async": "~0.2.6", + "source-map": "0.1.34", + "uglify-to-browserify": "~1.0.0", + "yargs": "~3.5.4" + } + }, + "wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", + "dev": true + }, + "yargs": { + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.5.4.tgz", + "integrity": "sha1-2K/49mXpTDS9JZvevRv68N3TU2E=", + "dev": true, + "requires": { + "camelcase": "^1.0.2", + "decamelize": "^1.0.0", + "window-size": "0.1.0", + "wordwrap": "0.0.2" + } + } + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "https-proxy-agent": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.2.tgz", + "integrity": "sha512-c8Ndjc9Bkpfx/vCJueCPy0jlP4ccCCSNDp8xwCZzPjKJUm+B+u9WX2x98Qx4n1PiMNTWo3D7KK5ifNV/yJyRzg==", + "dev": true, + "requires": { + "agent-base": "^4.3.0", + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=", + "dev": true + }, + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "dev": true, + "requires": { + "repeating": "^2.0.0" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true + }, + "interpret": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz", + "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==", + "dev": true + }, + "invert-kv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", + "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", + "dev": true + }, + "ipv4": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/ipv4/-/ipv4-1.0.4.tgz", + "integrity": "sha512-Hah1lDBolzIKGNAlA6izxkm/tKTpNi+aFXTu/qVOqxz1Ovxo74kdERGl+SYebX8hiAJ33OrwPyNyu/zza1SgcQ==", + "dev": true, + "requires": { + "address": "^1.0.3", + "chalk": "^1.1.3", + "copy-paste": "^1.3.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-callable": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", + "dev": true + }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", + "dev": true + }, + "is-finite": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", + "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "is-generator": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-generator/-/is-generator-1.0.3.tgz", + "integrity": "sha1-wUwhBX7TbjKNuANHlmxpP4hjifM=", + "dev": true + }, + "is-lower-case": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-lower-case/-/is-lower-case-1.1.3.tgz", + "integrity": "sha1-fhR75HaNxGbbO/shzGCzHmrWk5M=", + "dev": true, + "requires": { + "lower-case": "^1.1.0" + } + }, + "is-path-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", + "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", + "dev": true + }, + "is-path-in-cwd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", + "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", + "dev": true, + "requires": { + "is-path-inside": "^1.0.0" + } + }, + "is-path-inside": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", + "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", + "dev": true, + "requires": { + "path-is-inside": "^1.0.1" + } + }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "dev": true, + "requires": { + "has": "^1.0.1" + } + }, + "is-running": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-running/-/is-running-2.1.0.tgz", + "integrity": "sha1-MKc/9cw4VOT8JUkICen1q/jeCeA=", + "dev": true + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "is-symbol": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", + "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "dev": true, + "requires": { + "has-symbols": "^1.0.0" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "is-upper-case": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-upper-case/-/is-upper-case-1.1.2.tgz", + "integrity": "sha1-jQsfp+eTOh5YSDYA7H2WYcuvdW8=", + "dev": true, + "requires": { + "upper-case": "^1.1.0" + } + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true + }, + "is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true + }, + "jasmine": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-2.8.0.tgz", + "integrity": "sha1-awicChFXax8W3xG4AUbZHU6Lij4=", + "dev": true, + "requires": { + "exit": "^0.1.2", + "glob": "^7.0.6", + "jasmine-core": "~2.8.0" + } + }, + "jasmine-core": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.8.0.tgz", + "integrity": "sha1-vMl5rh+f0FcB5F5S5l06XWPxok4=", + "dev": true + }, + "jasminewd2": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/jasminewd2/-/jasminewd2-2.2.0.tgz", + "integrity": "sha1-43zwsX8ZnM4jvqcbIDk5Uka07E4=", + "dev": true + }, + "java-home": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/java-home/-/java-home-1.0.7.tgz", + "integrity": "sha512-nBowU+MqShJq6d8AEEGSYEijvAy4YXAc60cxyTRgZjt5v5lSzv7Eg2aoRJnGzFrXjyw+d0dN3rhQuuVMKzLf9g==", + "dev": true, + "requires": { + "xutil": "^1.0.2" + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "jszip": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.2.2.tgz", + "integrity": "sha512-NmKajvAFQpbg3taXQXr/ccS2wcucR1AZ+NtyWp2Nq7HHVsXhcJFR8p0Baf32C2yVvBylFWVeKf+WI2AnvlPhpA==", + "dev": true, + "requires": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "set-immediate-shim": "~1.0.1" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + }, + "dependencies": { + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + } + } + }, + "lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", + "dev": true + }, + "lcid": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "dev": true, + "requires": { + "invert-kv": "^2.0.0" + } + }, + "less": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/less/-/less-1.7.5.tgz", + "integrity": "sha1-TyIM9yiKJ+rKc5325ICKLUwNV1Y=", + "dev": true, + "requires": { + "clean-css": "2.2.x", + "graceful-fs": "~3.0.2", + "mime": "~1.2.11", + "mkdirp": "~0.5.0", + "request": "~2.40.0", + "source-map": "0.1.x" + }, + "dependencies": { + "asn1": { + "version": "0.1.11", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.1.11.tgz", + "integrity": "sha1-VZvhg3bQik7E2+gId9J4GGObLfc=", + "dev": true, + "optional": true + }, + "assert-plus": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz", + "integrity": "sha1-7nQAlBMALYTOxyGcasgRgS5yMWA=", + "dev": true, + "optional": true + }, + "async": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", + "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=", + "dev": true, + "optional": true + }, + "aws-sign2": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.5.0.tgz", + "integrity": "sha1-xXED96F/wDfwLXwuZLYC6iI/fWM=", + "dev": true, + "optional": true + }, + "combined-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz", + "integrity": "sha1-ATfmV7qlp1QcV6w3rF/AfXO03B8=", + "dev": true, + "optional": true, + "requires": { + "delayed-stream": "0.0.5" + } + }, + "delayed-stream": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz", + "integrity": "sha1-1LH0OpPoKW3+AmlPRoC8N6MTxz8=", + "dev": true, + "optional": true + }, + "forever-agent": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.5.2.tgz", + "integrity": "sha1-bQ4JxJIflKJ/Y9O0nF/v8epMUTA=", + "dev": true, + "optional": true + }, + "form-data": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-0.1.4.tgz", + "integrity": "sha1-kavXiKupcCsaq/qLwBAxoqyeOxI=", + "dev": true, + "optional": true, + "requires": { + "async": "~0.9.0", + "combined-stream": "~0.0.4", + "mime": "~1.2.11" + } + }, + "graceful-fs": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.11.tgz", + "integrity": "sha1-dhPHeKGv6mLyXGMKCG1/Osu92Bg=", + "dev": true, + "optional": true, + "requires": { + "natives": "^1.1.0" + } + }, + "http-signature": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-0.10.1.tgz", + "integrity": "sha1-T72sEyVZqoMjEh5UB3nAoBKyfmY=", + "dev": true, + "optional": true, + "requires": { + "asn1": "0.1.11", + "assert-plus": "^0.1.5", + "ctype": "0.5.3" + } + }, + "mime": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz", + "integrity": "sha1-WCA+7Ybjpe8XrtK32evUfwpg3RA=", + "dev": true, + "optional": true + }, + "mime-types": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-1.0.2.tgz", + "integrity": "sha1-mVrhOSq4r/y/yyZB3QVOlDwNXc4=", + "dev": true, + "optional": true + }, + "node-uuid": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz", + "integrity": "sha1-sEDrCSOWivq/jTL7HxfxFn/auQc=", + "dev": true, + "optional": true + }, + "oauth-sign": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.3.0.tgz", + "integrity": "sha1-y1QPk7srIqfVlBaRoojWDo6pOG4=", + "dev": true, + "optional": true + }, + "qs": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-1.0.2.tgz", + "integrity": "sha1-UKk+K1r2aRwxvOpdrnjubqGQN2g=", + "dev": true, + "optional": true + }, + "request": { + "version": "2.40.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.40.0.tgz", + "integrity": "sha1-TdZw9pbx5uhC5mtLXoOTAaub62c=", + "dev": true, + "optional": true, + "requires": { + "aws-sign2": "~0.5.0", + "forever-agent": "~0.5.0", + "form-data": "~0.1.0", + "hawk": "1.1.1", + "http-signature": "~0.10.0", + "json-stringify-safe": "~5.0.0", + "mime-types": "~1.0.1", + "node-uuid": "~1.4.0", + "oauth-sign": "~0.3.0", + "qs": "~1.0.0", + "stringstream": "~0.0.4", + "tough-cookie": ">=0.12.0", + "tunnel-agent": "~0.4.0" + } + }, + "source-map": { + "version": "0.1.43", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", + "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", + "dev": true, + "optional": true, + "requires": { + "amdefine": ">=0.0.4" + } + }, + "tunnel-agent": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz", + "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=", + "dev": true, + "optional": true + } + } + }, + "lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dev": true, + "requires": { + "immediate": "~3.0.5" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + }, + "longest": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", + "dev": true + }, + "loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "dev": true, + "requires": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + } + }, + "lower-case": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", + "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=", + "dev": true + }, + "lru-cache": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", + "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=", + "dev": true + }, + "make-error": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz", + "integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==", + "dev": true + }, + "map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "dev": true, + "requires": { + "p-defer": "^1.0.0" + } + }, + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true + }, + "map-stream": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", + "integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=", + "dev": true + }, + "marked": { + "version": "0.3.19", + "resolved": "https://registry.npmjs.org/marked/-/marked-0.3.19.tgz", + "integrity": "sha512-ea2eGWOqNxPcXv8dyERdSr/6FmzvWwzjMxpfGB/sbMccXoct+xY+YukPD+QTUZwyvK7BZwcr4m21WBOW41pAkg==", + "dev": true + }, + "mem": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", + "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", + "dev": true, + "requires": { + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^2.0.0", + "p-is-promise": "^2.0.0" + } + }, + "memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=", + "dev": true + }, + "meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "dev": true, + "requires": { + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, + "mime-db": { + "version": "1.40.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", + "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==", + "dev": true + }, + "mime-types": { + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", + "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "dev": true, + "requires": { + "mime-db": "1.40.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "mimoza": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/mimoza/-/mimoza-0.1.1.tgz", + "integrity": "sha1-6cZuKhW71B9ez5wWfmIiSB7LRy4=", + "dev": true + }, + "mincer": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/mincer/-/mincer-0.4.6.tgz", + "integrity": "sha1-XX71aiffsw4ZeXgxlIrvi2O4wT4=", + "dev": true, + "requires": { + "argparse": "~ 0.1.3", + "async": "~ 0.1.22", + "fs-tools": "~ 0.2.2", + "hike": "~ 0.1.1", + "mimoza": "~ 0.1.0", + "shellwords": "~ 0.1.0", + "types": "~ 0.1.0", + "underscore": "~ 1.3.3" + }, + "dependencies": { + "argparse": { + "version": "0.1.16", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-0.1.16.tgz", + "integrity": "sha1-z9AeD7uj1srtBJ+9dY1A9lGW9Xw=", + "dev": true, + "requires": { + "underscore": "~1.7.0", + "underscore.string": "~2.4.0" + }, + "dependencies": { + "underscore": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz", + "integrity": "sha1-a7rwh3UA02vjTsqlhODbn+8DUgk=", + "dev": true + } + } + }, + "async": { + "version": "0.1.22", + "resolved": "https://registry.npmjs.org/async/-/async-0.1.22.tgz", + "integrity": "sha1-D8GqoIig4+8Ovi2IMbqw3PiEUGE=", + "dev": true + } + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "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.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "moment": { + "version": "2.18.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.18.1.tgz", + "integrity": "sha1-w2GT3Tzhwu7SrbfIAtu8d6gbHA8=", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "mvn-artifact-filename": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mvn-artifact-filename/-/mvn-artifact-filename-3.0.2.tgz", + "integrity": "sha1-v+TCxENMMi112Dmysmzwei5NQmo=", + "dev": true + }, + "mvn-artifact-name-parser": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mvn-artifact-name-parser/-/mvn-artifact-name-parser-3.0.1.tgz", + "integrity": "sha512-BpENOFmZJIf6lN3fIYt14SOr69hUpfcWsQdDUAsyDL5jjfG9h+NkPg3ijTSumnu8GTfOX+io4KNj4asWvX67SA==", + "dev": true + }, + "mvn-artifact-url": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mvn-artifact-url/-/mvn-artifact-url-3.0.2.tgz", + "integrity": "sha1-zqOZeH00skB63uSR3VEpDcG2sdM=", + "dev": true, + "requires": { + "mvn-artifact-filename": "^3.0.2", + "node-fetch": "^1.7.2", + "xml2js": "^0.4.17" + }, + "dependencies": { + "node-fetch": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", + "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", + "dev": true, + "requires": { + "encoding": "^0.1.11", + "is-stream": "^1.0.1" + } + } + } + }, + "natives": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/natives/-/natives-1.1.6.tgz", + "integrity": "sha512-6+TDFewD4yxY14ptjKaS63GVdtKiES1pTPyxn9Jb0rBqPMZ7VcCiooEhPNsr+mqHtMGxa/5c/HhcC4uPEUw/nA==", + "dev": true, + "optional": true + }, + "ncp": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-0.6.0.tgz", + "integrity": "sha1-34zgIeJiviG1L+s9Plz6qxJJHw0=", + "dev": true + }, + "next-tick": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", + "dev": true + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "no-case": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", + "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", + "dev": true, + "requires": { + "lower-case": "^1.1.1" + } + }, + "nodejs": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/nodejs/-/nodejs-0.0.0.tgz", + "integrity": "sha1-RyL6LhisTrc6Qq4W0B41hKErdTE=" + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "npm-failsafe": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/npm-failsafe/-/npm-failsafe-0.2.2.tgz", + "integrity": "sha1-hgORytd/S9z+M2KxwEhKhdYrPfk=", + "dev": true, + "requires": { + "terminal-table-output": "^1.3.1" + } + }, + "npm-run-all": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", + "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "chalk": "^2.4.1", + "cross-spawn": "^6.0.5", + "memorystream": "^0.3.1", + "minimatch": "^3.0.4", + "pidtree": "^0.3.0", + "read-pkg": "^3.0.0", + "shell-quote": "^1.6.1", + "string.prototype.padend": "^3.0.0" + }, + "dependencies": { + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + } + } + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "open": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-6.4.0.tgz", + "integrity": "sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg==", + "dev": true, + "requires": { + "is-wsl": "^1.1.0" + } + }, + "opn": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.3.0.tgz", + "integrity": "sha512-bYJHo/LOmoTd+pfiYhfZDnf9zekVJrY+cnS2a5F2x+w5ppvTqObojTP7WiFG+kVZs9Inw+qQ/lw7TroWwhdd2g==", + "dev": true, + "requires": { + "is-wsl": "^1.1.0" + } + }, + "opn-cli": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/opn-cli/-/opn-cli-3.1.0.tgz", + "integrity": "sha1-+BmubK4LQRvQFJuFYP5siK2tIPg=", + "dev": true, + "requires": { + "file-type": "^3.6.0", + "get-stdin": "^5.0.1", + "meow": "^3.7.0", + "opn": "^4.0.0", + "temp-write": "^2.1.0" + }, + "dependencies": { + "opn": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/opn/-/opn-4.0.2.tgz", + "integrity": "sha1-erwi5kTf9jsKltWrfyeQwPAavJU=", + "dev": true, + "requires": { + "object-assign": "^4.0.1", + "pinkie-promise": "^2.0.0" + } + } + } + }, + "optimist": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "dev": true, + "requires": { + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" + } + }, + "os-locale": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", + "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", + "dev": true, + "requires": { + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", + "dev": true + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "p-is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", + "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", + "dev": true + }, + "p-limit": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", + "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "pako": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.10.tgz", + "integrity": "sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw==", + "dev": true + }, + "param-case": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-1.1.2.tgz", + "integrity": "sha1-3LCRpDwlm5Io8cNB57akTqC/l0M=", + "dev": true, + "requires": { + "sentence-case": "^1.1.2" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", + "dev": true + }, + "pascal-case": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-1.1.2.tgz", + "integrity": "sha1-Pl1kogBDgwp8STRMLXS0G+DJyZs=", + "dev": true, + "requires": { + "camel-case": "^1.1.1", + "upper-case-first": "^1.1.0" + }, + "dependencies": { + "camel-case": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-1.2.2.tgz", + "integrity": "sha1-Gsp8TRlTWaLOmVV5NDPG5VQlEfI=", + "dev": true, + "requires": { + "sentence-case": "^1.1.1", + "upper-case": "^1.1.1" + } + } + } + }, + "path-case": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/path-case/-/path-case-1.1.2.tgz", + "integrity": "sha1-UM5roNO+090LXCqcRVNpdDRAlRQ=", + "dev": true, + "requires": { + "sentence-case": "^1.1.2" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=" + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "path-to-regexp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.2.1.tgz", + "integrity": "sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ==" + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "pathval": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", + "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", + "dev": true + }, + "pause-stream": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", + "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", + "dev": true, + "requires": { + "through": "~2.3" + } + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true + }, + "pidtree": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.0.tgz", + "integrity": "sha512-9CT4NFlDcosssyg8KVFltgokyKZIFjoBxw8CTGy+5F38Y1eQWrt8tRayiUOXE+zVKQnYu5BR8JjCtvK3BcnBhg==", + "dev": true + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, + "postcss": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-2.2.6.tgz", + "integrity": "sha1-wENE4kSeRYa5Vfvkp093CA2EVx8=", + "dev": true, + "requires": { + "js-base64": "~2.1.5", + "source-map": "~0.1.40" + }, + "dependencies": { + "js-base64": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.1.9.tgz", + "integrity": "sha1-8OgK4DmkvWVLXygfyT8EqRSn/M4=", + "dev": true + }, + "source-map": { + "version": "0.1.43", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", + "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", + "dev": true, + "requires": { + "amdefine": ">=0.0.4" + } + } + } + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "protractor": { + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/protractor/-/protractor-5.4.2.tgz", + "integrity": "sha512-zlIj64Cr6IOWP7RwxVeD8O4UskLYPoyIcg0HboWJL9T79F1F0VWtKkGTr/9GN6BKL+/Q/GmM7C9kFVCfDbP5sA==", + "dev": true, + "requires": { + "@types/q": "^0.0.32", + "@types/selenium-webdriver": "^3.0.0", + "blocking-proxy": "^1.0.0", + "browserstack": "^1.5.1", + "chalk": "^1.1.3", + "glob": "^7.0.3", + "jasmine": "2.8.0", + "jasminewd2": "^2.1.0", + "optimist": "~0.6.0", + "q": "1.4.1", + "saucelabs": "^1.5.0", + "selenium-webdriver": "3.6.0", + "source-map-support": "~0.4.0", + "webdriver-js-extender": "2.1.0", + "webdriver-manager": "^12.0.6" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, + "webdriver-manager": { + "version": "12.1.5", + "resolved": "https://registry.npmjs.org/webdriver-manager/-/webdriver-manager-12.1.5.tgz", + "integrity": "sha512-f1apDjMpZ8SHlXtXGzqBxOjV+WQcDRz5PN7pWScgjXS7vhUIFcM3V89Shetf4A04n8DDR2MxiVQq6JproFcRZw==", + "dev": true, + "requires": { + "adm-zip": "^0.4.9", + "chalk": "^1.1.1", + "del": "^2.2.0", + "glob": "^7.0.3", + "ini": "^1.3.4", + "minimist": "^1.2.0", + "q": "^1.4.1", + "request": "^2.87.0", + "rimraf": "^2.5.2", + "semver": "^5.3.0", + "xml2js": "^0.4.17" + } + } + } + }, + "ps-tree": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.1.1.tgz", + "integrity": "sha512-kef7fYYSKVqQffmzTMsVcUD1ObNJMp8sNSmHGlGKsZQyL/ht9MZKk86u0Rd1NhpTOAuhqwKCLLpktwkqz+MF8A==", + "dev": true, + "requires": { + "event-stream": "=3.3.4" + } + }, + "psl": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.2.0.tgz", + "integrity": "sha512-GEn74ZffufCmkDDLNcl3uuyF/aSD6exEyh1v/ZSdAomB82t6G9hzJVRx0jBmLDW+VfZqks3aScmMw9DszwUalA==", + "dev": true + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "q": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz", + "integrity": "sha1-VXBbzZPF82c1MMLCy8DCs63cKG4=", + "dev": true + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "dev": true + }, + "range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + }, + "dependencies": { + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "^2.0.0" + } + } + } + }, + "rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "dev": true, + "requires": { + "resolve": "^1.1.6" + } + }, + "redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "dev": true, + "requires": { + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" + } + }, + "relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "dev": true, + "requires": { + "is-finite": "^1.0.0" + } + }, + "request": { + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "dev": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + } + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "resolve": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.1.tgz", + "integrity": "sha512-vIpgF6wfuJOZI7KKKSP+HmiKggadPQAdsp5HiC1mvqnfp0gF1vdwgBWZIdrVft9pgqoMFQN+R7BSWZiBxx+BBw==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "right-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", + "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", + "dev": true, + "requires": { + "align-text": "^0.1.1" + } + }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "saucelabs": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/saucelabs/-/saucelabs-1.5.0.tgz", + "integrity": "sha512-jlX3FGdWvYf4Q3LFfFWS1QvPg3IGCGWxIc8QBFdPTbpTJnt/v17FHXYVAn7C8sHf1yUXo2c7yIM0isDryfYtHQ==", + "dev": true, + "requires": { + "https-proxy-agent": "^2.2.1" + } + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + }, + "selenium-webdriver": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.6.0.tgz", + "integrity": "sha512-WH7Aldse+2P5bbFBO4Gle/nuQOdVwpHMTL6raL3uuBj/vPG07k6uzt3aiahu352ONBr5xXh0hDlM3LhtXPOC4Q==", + "dev": true, + "requires": { + "jszip": "^3.1.3", + "rimraf": "^2.5.4", + "tmp": "0.0.30", + "xml2js": "^0.4.17" + } + }, + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "dev": true + }, + "sentence-case": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-1.1.3.tgz", + "integrity": "sha1-gDSq/CFFdy06vhUJqkLJ4QQtwTk=", + "dev": true, + "requires": { + "lower-case": "^1.1.1" + } + }, + "serenity": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/serenity/-/serenity-1.1.6.tgz", + "integrity": "sha1-fEKeGhO7AQYnt9RgKZ7qv6HhMYQ=", + "dev": true, + "requires": { + "async": "^0.9.0", + "autoprefixer": "^3.0.0", + "clean-css": "^2.2.14", + "cli": "^0.6.4", + "ejs2": "^2.1.0", + "fs-extra": "^0.11.0", + "highlight.js": "^8.2.0", + "html-minifier": "^0.6.6", + "js-yaml": "^3.2.1", + "less": "^1.3.3", + "marked": "^0.3.2", + "mincer": "^0.4.6", + "send": "^0.8.3", + "uglify-js": "^2.4.15", + "walkdir": "0.0.7" + }, + "dependencies": { + "async": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", + "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=", + "dev": true + }, + "camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", + "dev": true + }, + "cliui": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "dev": true, + "requires": { + "center-align": "^0.1.1", + "right-align": "^0.1.1", + "wordwrap": "0.0.2" + } + }, + "debug": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-1.0.4.tgz", + "integrity": "sha1-W5wla9VLbsAigxdvqKDt5tFUy/g=", + "dev": true, + "requires": { + "ms": "0.6.2" + } + }, + "depd": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/depd/-/depd-0.4.4.tgz", + "integrity": "sha1-BwkfrnX5eCjYm0oCotR3jw58BmI=", + "dev": true + }, + "destroy": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.3.tgz", + "integrity": "sha1-tDO0ck5x/YVR2YhRdIUcX8N34sk=", + "dev": true + }, + "ee-first": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.0.5.tgz", + "integrity": "sha1-jJshKJjYzZ8alDZlDOe+ICyen/A=", + "dev": true + }, + "escape-html": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.1.tgz", + "integrity": "sha1-GBoobq05ejmpKFfPsdQwUuNWv/A=", + "dev": true + }, + "fresh": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.2.2.tgz", + "integrity": "sha1-lzHc9WeMf660T7kDxPct9VGH+nc=", + "dev": true + }, + "fs-extra": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.11.1.tgz", + "integrity": "sha1-3xBPlMyEHu+Pr+KkRsiPXTW7Lnk=", + "dev": true, + "requires": { + "jsonfile": "^2.0.0", + "mkdirp": "^0.5.0", + "ncp": "^0.6.0", + "rimraf": "^2.2.8" + } + }, + "jsonfile": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "mime": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz", + "integrity": "sha1-WCA+7Ybjpe8XrtK32evUfwpg3RA=", + "dev": true + }, + "ms": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.6.2.tgz", + "integrity": "sha1-2JwhJMb9wTU9Zai3e/GqxLGTcIw=", + "dev": true + }, + "on-finished": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.1.0.tgz", + "integrity": "sha1-DFOfCSkej/rd4MiiWFD7LO3HAi0=", + "dev": true, + "requires": { + "ee-first": "1.0.5" + } + }, + "range-parser": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.0.3.tgz", + "integrity": "sha1-aHKCNTXGkuLCoBA4Jq/YLC4P8XU=", + "dev": true + }, + "send": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/send/-/send-0.8.5.tgz", + "integrity": "sha1-N/cIIW5vUMF150xp/sU0hOL9gsc=", + "dev": true, + "requires": { + "debug": "1.0.4", + "depd": "0.4.4", + "destroy": "1.0.3", + "escape-html": "1.0.1", + "fresh": "0.2.2", + "mime": "1.2.11", + "ms": "0.6.2", + "on-finished": "2.1.0", + "range-parser": "~1.0.0" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "uglify-js": { + "version": "2.8.29", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", + "dev": true, + "requires": { + "source-map": "~0.5.1", + "uglify-to-browserify": "~1.0.0", + "yargs": "~3.10.0" + } + }, + "wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", + "dev": true + }, + "yargs": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "dev": true, + "requires": { + "camelcase": "^1.0.2", + "cliui": "^2.1.0", + "decamelize": "^1.0.0", + "window-size": "0.1.0" + } + } + } + }, + "serenity-cli": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/serenity-cli/-/serenity-cli-0.11.3.tgz", + "integrity": "sha512-mzYgIZ4+JvUv3Vgt3i9FCz4i5xLKd4b5dmIJVVYoqs/AkKvmw9s0POgCZgkT5X0nel8Apa9AfdqRd0X//jCu3g==", + "dev": true, + "requires": { + "ci-info": "1.1.3", + "java-home": "1.0.7", + "mkdirp": "0.5.1", + "mvn-artifact-filename": "3.0.2", + "mvn-artifact-name-parser": "3.0.1", + "mvn-artifact-url": "3.0.2", + "progress": "2.0.0", + "request": "2.87.0", + "rimraf": "2.6.2", + "split": "1.0.1", + "which": "1.3.1", + "winston": "2.4.0", + "yargs": "12.0.2" + }, + "dependencies": { + "ajv": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "dev": true, + "requires": { + "co": "^4.6.0", + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0" + } + }, + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, + "decamelize": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-2.0.0.tgz", + "integrity": "sha512-Ikpp5scV3MSYxY39ymh45ZLEecsTdv/Xj2CaQfI8RLMuwi7XvjX9H/fhraiSuU+C5w5NTDu4ZU72xNiZnurBPg==", + "dev": true, + "requires": { + "xregexp": "4.0.0" + } + }, + "fast-deep-equal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", + "dev": true + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, + "har-validator": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", + "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", + "dev": true, + "requires": { + "ajv": "^5.1.0", + "har-schema": "^2.0.0" + } + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", + "dev": true + }, + "oauth-sign": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", + "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 + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + }, + "request": { + "version": "2.87.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.87.0.tgz", + "integrity": "sha512-fcogkm7Az5bsS6Sl0sibkbhcKsnyon/jV1kF3ajGmF0c8HrttdKTPRT9hieOaQHA5HEq6r8OyWOo/o781C1tNw==", + "dev": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.6.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.5", + "extend": "~3.0.1", + "forever-agent": "~0.6.1", + "form-data": "~2.3.1", + "har-validator": "~5.0.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.17", + "oauth-sign": "~0.8.2", + "performance-now": "^2.1.0", + "qs": "~6.5.1", + "safe-buffer": "^5.1.1", + "tough-cookie": "~2.3.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.1.0" + } + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, + "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.0.5" + } + }, + "split": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", + "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", + "dev": true, + "requires": { + "through": "2" + } + }, + "tough-cookie": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", + "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==", + "dev": true, + "requires": { + "punycode": "^1.4.1" + } + }, + "yargs": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.2.tgz", + "integrity": "sha512-e7SkEx6N6SIZ5c5H22RTZae61qtn3PYUE8JYbBFlK9sYmh3DMQ6E5ygtaG/2BW0JZi4WGgTR2IV5ChqlqrDGVQ==", + "dev": true, + "requires": { + "cliui": "^4.0.0", + "decamelize": "^2.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^1.0.1", + "os-locale": "^3.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^10.1.0" + } + }, + "yargs-parser": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", + "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", + "dev": true, + "requires": { + "camelcase": "^4.1.0" + } + } + } + }, + "serenity-js": { + "version": "1.10.13", + "resolved": "https://registry.npmjs.org/serenity-js/-/serenity-js-1.10.13.tgz", + "integrity": "sha1-ZDChwZwNVQ/KSgs8bKsEOG27300=", + "dev": true, + "requires": { + "@serenity-js/core": ">= 1.6.0", + "@types/selenium-webdriver": "2.53.39", + "co": "4.6.0", + "glob": "7.1.1", + "graceful-fs": "4.1.11", + "is-generator": "1.0.3", + "lodash": "^4.17.5", + "selenium-webdriver": "3.0.1", + "ts-md5": "1.2.0", + "util-arity": "1.1.0" + }, + "dependencies": { + "@types/selenium-webdriver": { + "version": "2.53.39", + "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-2.53.39.tgz", + "integrity": "sha1-Ff+TOSwzmr051tOgTnFfqpomPPM=", + "dev": true + }, + "glob": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", + "integrity": "sha1-gFIR3wT6rxxjo2ADBs31reULLsg=", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.2", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true + }, + "selenium-webdriver": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.0.1.tgz", + "integrity": "sha1-ot6l2kqX9mcuiefKcnbO+jZRR6c=", + "dev": true, + "requires": { + "adm-zip": "^0.4.7", + "rimraf": "^2.5.4", + "tmp": "0.0.30", + "xml2js": "^0.4.17" + } + } + } + }, + "serve-handler": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.1.tgz", + "integrity": "sha512-LQPvxGia2TYqyMkHKH4jW9jx6jlQUMcWz6gJavZ3+4vsnB+SaWbYTncb9YsK5YBR6SlvyumREZJAzLw8VaFAUQ==", + "requires": { + "bytes": "3.0.0", + "content-disposition": "0.5.2", + "fast-url-parser": "1.1.3", + "mime-types": "2.1.18", + "minimatch": "3.0.4", + "path-is-inside": "1.0.2", + "path-to-regexp": "2.2.1", + "range-parser": "1.2.0" + }, + "dependencies": { + "mime-db": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", + "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==" + }, + "mime-types": { + "version": "2.1.18", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", + "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", + "requires": { + "mime-db": "~1.33.0" + } + } + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "set-immediate-shim": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", + "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", + "dev": true + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "shell-quote": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.1.tgz", + "integrity": "sha512-2kUqeAGnMAu6YrTPX4E3LfxacH9gKljzVjlkUeSqY0soGwK4KLl7TURXCem712tkhBCeeaFP9QK4dKn88s3Icg==", + "dev": true + }, + "shelljs": { + "version": "0.7.8", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.8.tgz", + "integrity": "sha1-3svPh0sNHl+3LhSxZKloMEjprLM=", + "dev": true, + "requires": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + } + }, + "shellwords": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", + "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", + "dev": true + }, + "sigmund": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", + "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "snake-case": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-1.1.2.tgz", + "integrity": "sha1-DC8l4wUVjZoY09l3BmGH/vilpmo=", + "dev": true, + "requires": { + "sentence-case": "^1.1.2" + } + }, + "sntp": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-0.2.4.tgz", + "integrity": "sha1-+4hfGLDzqtGJ+CSGJTa87ux1CQA=", + "dev": true, + "optional": true, + "requires": { + "hoek": "0.9.x" + } + }, + "source-map-support": { + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", + "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", + "dev": true, + "requires": { + "source-map": "^0.5.6" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "spdx-correct": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", + "dev": true + }, + "split": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", + "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=", + "dev": true, + "requires": { + "through": "2" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "dev": true, + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "stack-chain": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/stack-chain/-/stack-chain-1.3.7.tgz", + "integrity": "sha1-0ZLJ/06moiyUxN1FkXHj8AzqEoU=", + "dev": true + }, + "stack-generator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stack-generator/-/stack-generator-1.1.0.tgz", + "integrity": "sha1-NvapIHUabBD0maE8Msu19RoLiyU=", + "dev": true, + "requires": { + "stackframe": "^1.0.2" + }, + "dependencies": { + "stackframe": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.0.4.tgz", + "integrity": "sha512-to7oADIniaYwS3MhtCa/sQhrxidCCQiF/qp4/m5iN3ipf0Y7Xlri0f6eG29r08aL7JYl8n32AF3Q5GYBZ7K8vw==", + "dev": true + } + } + }, + "stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=", + "dev": true + }, + "stackframe": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-0.3.1.tgz", + "integrity": "sha1-M6qE8Rd6VUjIk1Uzy/6zQgl19aQ=", + "dev": true + }, + "stacktrace-gps": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/stacktrace-gps/-/stacktrace-gps-2.4.4.tgz", + "integrity": "sha1-acgn6dbW9Bz0ONfxleLjy/zyjEQ=", + "dev": true, + "requires": { + "source-map": "0.5.6", + "stackframe": "~0.3" + }, + "dependencies": { + "source-map": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=", + "dev": true + } + } + }, + "stacktrace-js": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/stacktrace-js/-/stacktrace-js-1.3.1.tgz", + "integrity": "sha1-Z8qyWJr1xBe5Yvc2mUAne7O2oYs=", + "dev": true, + "requires": { + "error-stack-parser": "^1.3.6", + "stack-generator": "^1.0.7", + "stacktrace-gps": "^2.4.3" + } + }, + "stream-combiner": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", + "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=", + "dev": true, + "requires": { + "duplexer": "~0.1.1" + } + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "string.prototype.padend": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.0.0.tgz", + "integrity": "sha1-86rvfBcZ8XDF6rHDK/eA2W4h8vA=", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.4.3", + "function-bind": "^1.0.2" + } + }, + "stringstream": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.6.tgz", + "integrity": "sha512-87GEBAkegbBcweToUrdzf3eLhWNg06FJTebl4BVJz/JgWy8CvEr9dRtX5qWphiynMSQlxxi+QqN0z5T32SLlhA==", + "dev": true, + "optional": true + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "^0.2.0" + } + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true + }, + "strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "dev": true, + "requires": { + "get-stdin": "^4.0.1" + }, + "dependencies": { + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "dev": true + } + } + }, + "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": "4.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", + "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", + "dev": true, + "requires": { + "has-flag": "^2.0.0" + } + }, + "swap-case": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/swap-case/-/swap-case-1.1.2.tgz", + "integrity": "sha1-w5IDpFhzhfrTyFCgvRvK+ggZdOM=", + "dev": true, + "requires": { + "lower-case": "^1.1.1", + "upper-case": "^1.1.1" + } + }, + "sync-exec": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/sync-exec/-/sync-exec-0.6.2.tgz", + "integrity": "sha1-cX0izFPwzh3vVZQ2LzqJouu5EQU=", + "dev": true, + "optional": true + }, + "temp-fs": { + "version": "0.9.9", + "resolved": "https://registry.npmjs.org/temp-fs/-/temp-fs-0.9.9.tgz", + "integrity": "sha1-gHFzBDeHByDpQxUy/igUNk+IA9c=", + "dev": true, + "requires": { + "rimraf": "~2.5.2" + }, + "dependencies": { + "rimraf": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.4.tgz", + "integrity": "sha1-loAAk8vxoMhr2VtGJUZ1NcKd+gQ=", + "dev": true, + "requires": { + "glob": "^7.0.5" + } + } + } + }, + "temp-write": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/temp-write/-/temp-write-2.1.0.tgz", + "integrity": "sha1-WYkJGODvCdVIqqNC9L00CdhATpY=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "mkdirp": "^0.5.0", + "os-tmpdir": "^1.0.0", + "pify": "^2.2.0", + "pinkie-promise": "^2.0.0", + "uuid": "^2.0.1" + }, + "dependencies": { + "uuid": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", + "integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho=", + "dev": true + } + } + }, + "terminal-table-output": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/terminal-table-output/-/terminal-table-output-1.3.2.tgz", + "integrity": "sha1-tc1vKJUvVoS3ScPVgit3DK5vR+I=", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "title-case": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/title-case/-/title-case-1.1.2.tgz", + "integrity": "sha1-+uSmrlRr+iLQg6DuqRCkDRLtT1o=", + "dev": true, + "requires": { + "sentence-case": "^1.1.1", + "upper-case": "^1.0.3" + } + }, + "tmp": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.30.tgz", + "integrity": "sha1-ckGdSovn1s51FI/YsyTlk6cRwu0=", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.1" + } + }, + "tough-cookie": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "dev": true, + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + } + } + }, + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", + "dev": true + }, + "ts-md5": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ts-md5/-/ts-md5-1.2.0.tgz", + "integrity": "sha1-wws4UmG9J5YvdUUJZ18OlXiBBWk=", + "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.0", + "chalk": "^2.0.0", + "diff": "^3.1.0", + "make-error": "^1.1.1", + "minimist": "^1.2.0", + "mkdirp": "^0.5.1", + "source-map-support": "^0.4.0", + "tsconfig": "^6.0.0", + "v8flags": "^3.0.0", + "yn": "^2.0.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, + "ts-retry": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/ts-retry/-/ts-retry-1.0.7.tgz", + "integrity": "sha512-wTz1uXKIRLGrscbM1s0Kax1A/DrhY6pU7aUDEHvev36ImvS6V7I1tL9nypEpxym6OihvPbpugSihgI+T/s4ALw==", + "dev": true + }, + "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.0" + }, + "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 + } + } + }, + "tslib": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", + "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==", + "dev": true + }, + "tslint": { + "version": "5.18.0", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.18.0.tgz", + "integrity": "sha512-Q3kXkuDEijQ37nXZZLKErssQVnwCV/+23gFEMROi8IlbaBG6tXqLPQJ5Wjcyt/yHPKBC+hD5SzuGaMora+ZS6w==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "builtin-modules": "^1.1.1", + "chalk": "^2.3.0", + "commander": "^2.12.1", + "diff": "^3.2.0", + "glob": "^7.1.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "resolve": "^1.3.2", + "semver": "^5.3.0", + "tslib": "^1.8.0", + "tsutils": "^2.29.0" + } + }, + "tsutils": { + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", + "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true + }, + "type": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/type/-/type-1.0.1.tgz", + "integrity": "sha512-MAM5dBMJCJNKs9E7JXo4CXRAansRfG0nlJxW7Wf6GZzSOvH31zClSaHdIMWLehe/EGMBkqeC55rrkaOr5Oo7Nw==", + "dev": true + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, + "types": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/types/-/types-0.1.1.tgz", + "integrity": "sha1-hgxoWdETZik/g12Mla68+VApg44=", + "dev": true + }, + "typescript": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.2.tgz", + "integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==", + "dev": true + }, + "uglify-to-browserify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", + "dev": true + }, + "underscore": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.3.3.tgz", + "integrity": "sha1-R6xTaD2vgyv6lS4XdEF9pHgXrkI=", + "dev": true + }, + "underscore.string": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.4.0.tgz", + "integrity": "sha1-jN2PusTi0uoefi6Al8QvRCKA+Fs=", + "dev": true + }, + "upper-case": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", + "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=", + "dev": true + }, + "upper-case-first": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-1.1.2.tgz", + "integrity": "sha1-XXm+3P8UQZUY/S7bCgUHybaFkRU=", + "dev": true, + "requires": { + "upper-case": "^1.1.1" + } + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "util-arity": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/util-arity/-/util-arity-1.1.0.tgz", + "integrity": "sha1-WdAa8f2z/t4KxOYysKtfbOl8kzA=", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", + "dev": true + }, + "v8flags": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.1.3.tgz", + "integrity": "sha512-amh9CCg3ZxkzQ48Mhcb8iX7xpAfYJgePHxWMQCBWECpOSqJUXgY26ncA61UTV0BkPqfhcy6mzwCIoP4ygxpW8w==", + "dev": true, + "requires": { + "homedir-polyfill": "^1.0.1" + } + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "walkdir": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/walkdir/-/walkdir-0.0.7.tgz", + "integrity": "sha1-BNoCcKh6d4VAFzzb8KLbSZqNnik=", + "dev": true + }, + "webdriver-js-extender": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/webdriver-js-extender/-/webdriver-js-extender-2.1.0.tgz", + "integrity": "sha512-lcUKrjbBfCK6MNsh7xaY2UAUmZwe+/ib03AjVOpFobX4O7+83BUveSrLfU0Qsyb1DaKJdQRbuU+kM9aZ6QUhiQ==", + "dev": true, + "requires": { + "@types/selenium-webdriver": "^3.0.0", + "selenium-webdriver": "^3.0.1" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "window-size": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", + "dev": true + }, + "winston": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-2.4.0.tgz", + "integrity": "sha1-gIBQuT1SZh7Z+2wms/DIJnCLCu4=", + "dev": true, + "requires": { + "async": "~1.0.0", + "colors": "1.0.x", + "cycle": "1.0.x", + "eyes": "0.1.x", + "isstream": "0.1.x", + "stack-trace": "0.0.x" + }, + "dependencies": { + "async": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz", + "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=", + "dev": true + }, + "colors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", + "dev": true + } + } + }, + "wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", + "dev": true + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "xml2js": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", + "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", + "dev": true, + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~9.0.1" + } + }, + "xmlbuilder": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", + "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=", + "dev": true + }, + "xregexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.0.0.tgz", + "integrity": "sha512-PHyM+sQouu7xspQQwELlGwwd05mXUFqwFYfqPO0cC7x4fxyHnnuetmQr6CjJiafIDoH4MogHb9dOoJzR/Y4rFg==", + "dev": true + }, + "xutil": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/xutil/-/xutil-1.0.11.tgz", + "integrity": "sha512-gGPrnfcP64684kJbxdHCH0g/I+4dfyeJJL+wJR1jKJyMSJCSP1qaMcjFlymY0PV9uPUEApnTw+Ys7ueM9HLUNQ==", + "dev": true, + "requires": { + "chalk": "^2.3.0", + "detect-port": "^1.2.2", + "ipv4": "^1.0.2", + "lodash": "^4.17.4", + "mkdirp": "~0.5.1", + "moment": "~2.18.1", + "opn": "^5.1.0", + "rimraf": "^2.5.4", + "semver": "^5.4.1", + "shelljs": "^0.7.8", + "uuid": "^3.1.0" + } + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, + "yn": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz", + "integrity": "sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=", + "dev": true + } + } +} diff --git a/test-e2e/package.json b/test-e2e/package.json new file mode 100755 index 000000000..b77dc176a --- /dev/null +++ b/test-e2e/package.json @@ -0,0 +1,50 @@ +{ + "name": "blockstack-sernityjs-test-e2e", + "version": "1.0.0", + "dependencies": { + "http-server": "^0.12.0" + }, + "scripts": { + "clean": "rimraf target", + "serenity:update": "serenity update", + "webdriver:update": "webdriver-manager update --versions.chrome=78.0.3904.97", + "bs-start": "node src/browserstack-local/start-browserstack-local.js", + "bs-stop": "node src/browserstack-local/stop-browserstack-local.js", + "app-start": "serve ./../build -l 5757 --single &", + "helloblockstack-start": "node features/hello-blockstack-app/server.js &", + "protractor": "protractor protractor.conf.js", + "pretest": "serenity update && npm run app-start", + "report": "serenity run", + "test-e2e": "npm run app-start && npm run helloblockstack-start && failsafe clean serenity:update webdriver:update protractor report", + "test-e2e:browserstack": "npm run app-start && npm run helloblockstack-start && failsafe clean serenity:update bs-start protractor report bs-stop", + "posttest": "pkill -f http-server" + }, + "devDependencies": { + "@types/chai": "^4.1.7", + "@types/chai-as-promised": "^7.1.0", + "@types/core-js": "^2.5.2", + "@types/cucumber": "^4.0.6", + "@types/express": "^4.17.0", + "@types/mocha": "^5.2.7", + "@types/node": "^8.10.49", + "browserstack-local": "^1.4.2", + "chai": "^4.2.0", + "chai-as-promised": "^7.1.1", + "cucumber": "^1.3.3", + "cypress-serenity-reporter": "^1.0.1", + "npm-failsafe": "^0.4.1", + "open": "^6.4.0", + "opn-cli": "^3.1.0", + "protractor": "5.4.2", + "rimraf": "^2.6.2", + "serenity": "^1.1.6", + "serenity-cli": "^0.11.3", + "serenity-js": "^1.10.13", + "ts-node": "^3.3.0", + "ts-retry": "^1.0.7", + "tslint": "^5.7.0", + "typescript": "^2.9.2", + "serve-handler": "^6.1.1", + "serve": "^10.1.2" + } +} diff --git a/test-e2e/protractor.conf.js b/test-e2e/protractor.conf.js new file mode 100755 index 000000000..7f0604977 --- /dev/null +++ b/test-e2e/protractor.conf.js @@ -0,0 +1,2 @@ +var config = require('./conf'); +exports.config = config; diff --git a/test-e2e/run-parallel.js b/test-e2e/run-parallel.js deleted file mode 100644 index ec6c51627..000000000 --- a/test-e2e/run-parallel.js +++ /dev/null @@ -1,70 +0,0 @@ -const Mocha = require('mocha'); -const fs = require('fs'); -const path = require('path'); -const { promisify } = require('util'); -const mochaSteps = require('mocha-steps'); -const PQueue = require('p-queue'); - -const mocha = new Mocha({ - timeout: 200000 -}); - -mocha.files = Mocha.utils - .lookupFiles(__dirname, [ 'js' ], true) - .filter(f => path.basename(f) !== path.basename(__filename)) - .sort(); - -mocha.loadFiles(); - -const testSuites = mocha.suite.suites; -const rootRunner = new Mocha.Runner(mocha.suite); -const queue = new PQueue({ concurrency: 5 }); - -async function start() { - - await promisify(rootRunner.hook).call(rootRunner, 'beforeAll'); - - testSuites.forEach(suite => { - - queue.add(async () => { - await promisify(rootRunner.hook).call(rootRunner, 'beforeEach'); - - const runner = new Mocha.Runner(suite); - runner.addListener('test', (test) => { - //console.log(`Test started: ${test.fullTitle()}`); - }); - runner.addListener('pass', (test) => { - //console.log(`Test passed: ${test.fullTitle()}`); - }); - runner.addListener('fail', (test, error) => { - console.error(`Test failed: ${test.fullTitle()} - ${error}`); - }); - runner.addListener('suite', (s) => { - console.log(`Suite started: ${s.fullTitle()}`); - }); - runner.addListener('suite end', (s) => { - console.log(`Suite ended: ${s.fullTitle()}`); - }); - - await new Promise(res => { - runner.run((failures) => { - setTimeout(() => res(), 2000); - }); - }); - - await promisify(rootRunner.hook).call(rootRunner, 'afterEach'); - }); - - }); - - await queue.onIdle(); - - await promisify(rootRunner.hook).call(rootRunner, 'afterAll'); - -} - -start().then(() => { - console.log('Finished'); -}).catch(err => { - console.error(`Finished with error: ${err}`); -}); diff --git a/test-e2e/src/browserstack-local/start-browserstack-local.js b/test-e2e/src/browserstack-local/start-browserstack-local.js new file mode 100755 index 000000000..c3a6d8b5c --- /dev/null +++ b/test-e2e/src/browserstack-local/start-browserstack-local.js @@ -0,0 +1,22 @@ +/* eslint no-console: ["error", { allow: ["warn", "error","log"] }] */ +const browserstack = require("browserstack-local"); +const bsLocal = new browserstack.Local(); + +/* replace with your key. + You can also set an environment variable - "BROWSERSTACK_ACCESS_KEY". + */ +const BROWSERSTACK_AUTH = 'BROWSERSTACK_AUTH'; +const RANDOM_STRING = 'RANDOM_STRING'; + +const bsLocalArgs = { + key: process.env[BROWSERSTACK_AUTH].trim().split(/:(.+)/)[1], + "local-identifier": process.env[RANDOM_STRING], + force: "true", + verbose: "true", +}; + +bsLocal.start(bsLocalArgs, (error) => { + if (error) return console.error(error); + console.log("Started BrowserStackLocal"); + return null; +}); diff --git a/test-e2e/src/browserstack-local/stop-browserstack-local.js b/test-e2e/src/browserstack-local/stop-browserstack-local.js new file mode 100755 index 000000000..cd7e26ac2 --- /dev/null +++ b/test-e2e/src/browserstack-local/stop-browserstack-local.js @@ -0,0 +1,11 @@ +/* eslint no-console: ["error", { allow: ["warn", "error","log"] }] */ +const browserstack = require("browserstack-local"); + +const bsLocal = new browserstack.Local(); + +// stop the Local instance +bsLocal.stop((error) => { + if (error) return console.error(error); + console.log("Stopped BrowserStackLocal"); + return null; +}); diff --git a/test-e2e/src/start-server.js b/test-e2e/src/start-server.js new file mode 100644 index 000000000..19b934ff8 --- /dev/null +++ b/test-e2e/src/start-server.js @@ -0,0 +1,23 @@ +/* eslint no-console: ["error", { allow: ["warn", "error","log"] }] */ +const path = require('path') +const url = require('url') +const { createServer: createHttpServer, Server: HttpServer } = require('http') +const serveHandler = require('serve-handler') +const browserHostUrl = 'http://localhost:5757' +const serveDirectory = path.resolve('./../build') + + +let staticWebServer = createHttpServer((req, res) => { + return serveHandler(req, res, { + public: serveDirectory, + rewrites: [{ source: '**', destination: '/index.html' }] + }) +}) +staticWebServer.listen(url.parse(browserHostUrl).port, error => { + if (error) { + console.error(`Error starting web server: ${error}`) + } else { + console.log(`Web server started at http://localhost:${staticWebServer.address().port}`) + return null; + } +}) diff --git a/test-e2e/src/utils/Utils.ts b/test-e2e/src/utils/Utils.ts new file mode 100755 index 000000000..d07694308 --- /dev/null +++ b/test-e2e/src/utils/Utils.ts @@ -0,0 +1,180 @@ +import {browser, ElementFinder, ExpectedConditions, WebElement} from 'protractor'; + +export class Utils { + + /** + * Retries a given function until the given time has elapsed. + * @param {(Promise|(function():any)} func + * @param {number} timeout - milliseconds to continue re-trying execution of a failing function. + * @param {number} poll - milliseconds to wait before trying a failed function. + */ + static async retry(func: any, timeout: number, poll: number): Promise { + const startTime = Date.now(); + let lastErr; + const hasTimeRemaining = () => (Date.now() - startTime < timeout); + while (hasTimeRemaining()) { + try { + return await Promise.resolve(func()); + } catch (err) { + lastErr = err; + if (hasTimeRemaining()) { + await browser.sleep(poll); + if (hasTimeRemaining()) { + console.warn(`Retrying after getting error: ${err}`); + continue; + } + } + break; + } + } + console.error(`Error after timeout period has elapsed.`); + throw lastErr; + } + + static async getPlatform(): Promise { + const session = await browser.getSession(); + const platform = await session.getCapabilities().get("platform"); + return platform; + } + + static async getSessionID(): Promise { + const session = await browser.getSession(); + return session.getId(); + } + + /** + * This uses Selenium's `manage().logs()` API which is now only supported by Chrome's + * WebDriver. Some remote WebDriver services (like BrowserStack) shim support for + * this API on their end for some web browsers. Expect this to throw various kinds of + * Errors, or silently failing with an empty result - depending on the environment and browser. + */ + static async getBrowserLogs(): Promise { + const logEntries = await browser.manage().logs().get('browser'); + if (!logEntries) { + throw new Error('Not supported - falsy `logs` object returned'); + } + const logs = logEntries.map((entry) => { + entry.toJSON(); + }); + const logJson = JSON.stringify(logs, null, 2); + return logJson; + } + + /** + * @param {WebElement} element + */ + static async scrollIntoView(targetElement: ElementFinder): Promise { + await browser.executeScript('arguments[0].scrollIntoView(true);', targetElement); + } + + /** + * Waits until the element is both located and visible, and scrolls the element into view. + * @param {!(By|Function)} locator The locator to use. + * @returns {Promise} + */ + static async waitForElement(targetElement: ElementFinder, {timeout = 40000, poll = 200, driverWait = 20000} = {}): Promise { + return this.retry(async () => { + await browser.wait(ExpectedConditions.presenceOf(targetElement), driverWait); + await browser.wait(ExpectedConditions.visibilityOf(targetElement), driverWait); + await this.scrollIntoView(targetElement); + return targetElement; + }, timeout, poll); + } + + /** + * Loops with try/catch around the locator and click function for specified amount of tries. + * @param {!(By|Function)} locator The locator to use. + */ + static async click(targetElement: ElementFinder, {timeout = 15000, poll = 200, driverWait = 2500} = {}): Promise { + const elm = await Utils.waitForElement(targetElement, {timeout, poll, driverWait}); + await elm.click(); + } + + /** + * Loops with try/catch around the locator and click function for specified amount of tries. + * @param {!(By|Function)} locator The locator to use. + */ + static async sendKeys(targetElement: ElementFinder, value: string, {timeout = 15000, poll = 200, driverWait = 2500} = {}): Promise { + const elm = await Utils.waitForElement(targetElement, {timeout, poll, driverWait}); + const capabilities = await browser.getCapabilities(); + const browserName = capabilities.get('browserName'); + console.log(`CURRENT browserName:${browserName}`); + // Bug on iOS devices with selenium/appium & react + // See: https://github.com/facebook/react/issues/11488#issuecomment-347775628 + // Updated to use less hacky workaround from cypress: + // See: https://github.com/cypress-io/cypress/pull/732/files#diff-ed17d49edc10403752ba3d786a7512dbR34 + if (browserName === 'safari') { + await browser.executeScript(` + let input = arguments[0]; + if (input.tagName === "INPUT") { + let valueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value").set; + valueSetter.call(input, arguments[1]); + } else { + let valueSetter = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, "value").set; + valueSetter.call(input, arguments[1]); + } + input.dispatchEvent(new Event('input', { bubbles: true })); + `, elm, value); + await browser.sleep(500); + } + else { + await elm.sendKeys(value); + } + } + + static async waitForElementToDisappear(targetElement: ElementFinder): Promise { + try { + let isDisplayed = false; + for (let i = 0; i < 500; i++) { + await browser.sleep(2000); + isDisplayed = await targetElement.isDisplayed(); + if (isDisplayed === false) { + console.log("Spinner is displaying. : " + isDisplayed); + return; + } + } + } catch (error) { + + } + } + + /** + * + * @param {string} script A javascript expression that evaluates to a promise. + * @param {...any} args + * @returns {any} The evaluated promise result, if any. + */ + // tslint:disable-next-line: align + static async executePromise(script, ...args) { + try{ + return browser.executeAsyncScript(` + var callback = arguments[arguments.length - 1]; + ${script} + .then(result => callback([result])) + `, ...args); + } + catch(e){ + return browser.executeAsyncScript(` + var callback = arguments[arguments.length - 1]; + ${script} + .then(result => callback([result])) + `, ...args); + } + // tslint:disable-next-line: curly + } + + static async waitForElementToDisplayed(targetElement: ElementFinder): Promise { + try { + let isDisplayed = false; + for (let i = 0; i < 500; i++) { + await browser.sleep(2000); + isDisplayed = await targetElement.isDisplayed(); + if (isDisplayed === true) { + return; + } + } + } catch (error) { + + } + } +} diff --git a/test-e2e/utils/browserstack-environments.js b/test-e2e/src/utils/browserstack-environments.js old mode 100644 new mode 100755 similarity index 61% rename from test-e2e/utils/browserstack-environments.js rename to test-e2e/src/utils/browserstack-environments.js index 3e0ac32c3..aa4d2b76c --- a/test-e2e/utils/browserstack-environments.js +++ b/test-e2e/src/utils/browserstack-environments.js @@ -1,46 +1,45 @@ module.exports = [ - - // Windows + //Windows { - 'desc': 'Win10-Chrome-71.0', + 'name': 'Win10-Chrome-71.0', 'browserName': 'Chrome', 'browser_version': '71.0', 'os': 'Windows', 'os_version': '10', 'resolution': '1024x768' }, { - 'desc': 'Win10-Firefox-64.0', + 'name': 'Win10-Firefox-64.0', 'browserName' : 'Firefox', 'browser_version' : '64.0', 'os' : 'Windows', 'os_version' : '10', 'resolution' : '1024x768', }, { - 'desc': 'Win10-Edge-18.0', + 'name': 'Win10-Edge-18.0', 'browserName' : 'Edge', 'browser_version' : '18.0', 'os' : 'Windows', 'os_version' : '10', 'resolution' : '1024x768', - }, + }, // macOS { - 'desc': 'macOS-10.14-Mojave-Chrome-71.0', + 'name': 'macOS-10.14-Mojave-Chrome-71.0', 'browserName' : 'Chrome', 'browser_version' : '71.0', 'os' : 'OS X', 'os_version' : 'Mojave', 'resolution' : '1024x768', }, { - 'desc': 'macOS-10.14-Mojave-Firefox-64.0', + 'name': 'macOS-10.14-Mojave-Firefox-64.0', 'browserName' : 'Firefox', 'browser_version' : '64.0', 'os' : 'OS X', 'os_version' : 'Mojave', 'resolution' : '1024x768', }, { - 'desc': 'macOS-10.14-Mojave-Safari-12.0', + 'name': 'macOS-10.14-Mojave-Safari-12.0', 'browserName' : 'Safari', 'browser_version' : '12.0', 'os' : 'OS X', @@ -48,36 +47,39 @@ module.exports = [ 'resolution' : '1024x768', }, - // iOS - { - 'desc': 'iOS-12-iPhone-XS', - 'browserName' : 'iPhone', - 'device' : 'iPhone XS', - 'realMobile' : 'true', - 'os_version' : '12', - }, { - 'desc': 'iOS-11-iPhone-8', - 'browserName' : 'iPhone', - 'device' : 'iPhone 8', - 'realMobile' : 'true', - 'os_version' : '11' - }, + //iOS + { + 'name': 'iOS-12-iPhone-XS', + 'browserName' : 'iPhone', + 'device' : 'iPhone XS', + 'realMobile' : 'true', + 'os_version' : '12', + 'sendKeyStrategy':'oneByOne' + }, + // { + // 'name': 'iOS-11-iPhone-8', + // 'browserName' : 'iPhone', + // 'device' : 'iPhone 8', + // 'realMobile' : 'true', + // 'os_version' : '11', + // 'sendKeyStrategy':'oneByOne' + // }, - // Android + //Android { - 'desc': 'Android-9.0-Google-Pixel-3', + 'name': 'Android-9.0-Google-Pixel-3', 'browserName' : 'android', 'device' : 'Google Pixel 3', 'realMobile' : 'true', 'os_version' : '9.0', }, { - 'desc': 'Android-6.0-Google-Nexus-6', + 'name': 'Android-6.0-Google-Nexus-6', 'browserName' : 'android', 'device' : 'Google Nexus 6', 'realMobile' : 'true', 'os_version' : '6.0', }, { - 'desc': 'Android-8.0-Samsung-Galaxy-S9', + 'name': 'Android-8.0-Samsung-Galaxy-S9', 'browserName' : 'android', 'device' : 'Samsung Galaxy S9', 'realMobile' : 'true', diff --git a/test-e2e/src/utils/can-open-protocol.ts b/test-e2e/src/utils/can-open-protocol.ts new file mode 100755 index 000000000..e5be5b629 --- /dev/null +++ b/test-e2e/src/utils/can-open-protocol.ts @@ -0,0 +1,78 @@ + +import { execSync } from 'child_process'; + +/** @type {boolean} */ +let cachedValue = undefined; + +/** + * Queries the local system for the ability to open the `blockstack:` protocol. + * As in, determines if the native Blockstack Browser app is installed. + * Note: Currently only supported on MacOS. Always returns false on other systems. + * @returns {boolean} Returns true if the protocol handler is registered. + */ + +export class Canopenprotocol{ + + static async canOpenProtocol() { + if (cachedValue !== undefined) { + return cachedValue; + } + return cachedValue = (() => { + try { + switch (process.platform) { + case 'darwin': return Canopenprotocol; + default: return false; + } + } catch (error) { + console.log(`Error trying to detect protocol handler: ${error}`); + return false; + } + })(); + } + + /** + * For more details.. + * @see https://superuser.com/a/413606 + * @see https://github.com/nwjs/nw.js/issues/951#issuecomment-130117544 + */ + + static async canOpenProtocolDarwin() { + const launchServicesDir = '/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support'; + + const lsregisterDump = () => execSync(`${launchServicesDir}/lsregister -dump`, { encoding: 'utf8' }); + + const checkIsHandlerRegistered = async () => { + const stdout = lsregisterDump(); + const hasHandler = /bindings:\s+blockstack:/.test(stdout); + return hasHandler; + }; + + const clearRegisteredHandlers = async () => { + const stdout = lsregisterDump(); + const pathRegex = /path:\s+(.*?)\/Blockstack.app\n/g; + let match = null; +// tslint:disable-next-line: no-conditional-assignment + while (match = pathRegex.exec(stdout)) { + const appPath = `${match[1]}/Blockstack.app` + await execSync(`${launchServicesDir}/lsregister -u "${appPath}"`, { encoding: 'utf8' }); + } + }; + + const isHandlerRegistered = checkIsHandlerRegistered(); + if (isHandlerRegistered) { + // Try clearing out the handler registrations + console.warn('Notice: Clearing the native blockstack protocol handler from the local system. To restore, just re-open Blockstack.app'); + clearRegisteredHandlers(); + // Then check again and return the result. + const isStillRegistered = checkIsHandlerRegistered(); + if (isStillRegistered) { + console.warn('Was unable to clear the native protocol handler registration from the system.') + } + return isStillRegistered; + } else { + return false; + } + + } + +} \ No newline at end of file diff --git a/test-e2e/utils/fast-selenium.js b/test-e2e/src/utils/fast-selenium.js old mode 100644 new mode 100755 similarity index 100% rename from test-e2e/utils/fast-selenium.js rename to test-e2e/src/utils/fast-selenium.js diff --git a/test-e2e/utils/helpers.js b/test-e2e/src/utils/helpers.js old mode 100644 new mode 100755 similarity index 97% rename from test-e2e/utils/helpers.js rename to test-e2e/src/utils/helpers.js index 6eeb93ccf..c6ecaeba3 --- a/test-e2e/utils/helpers.js +++ b/test-e2e/src/utils/helpers.js @@ -13,7 +13,7 @@ module.exports = class Helpers { return (Math.floor(Math.random() * (max - min)) + min); } - static getRandomString(length = 20) { + static getRandomString(length = 20){ let str = ''; do { str += Math.random().toString(36).substr(2) } while (str.length < length) return str.substr(0, length); diff --git a/test-e2e/src/utils/sample-account.ts b/test-e2e/src/utils/sample-account.ts new file mode 100755 index 000000000..7dd996536 --- /dev/null +++ b/test-e2e/src/utils/sample-account.ts @@ -0,0 +1,15 @@ +export class SampleAccount { + static RECOVERY_ID = 'test_e2e_recovery'; + static PASSWORD = '7p7M4vu89xMn964AE6T7'; + static EMAIL = 'test_e2e_recovery@mail-apps.com'; + static SECRET_RECOVERY_KEY = 'layer decrease junk moral access kid say model enter rigid spend simple'; + static MAGIC_RECOVERY_CODE = '6SEAA7SaeQKTnrkcr2CBxRpD8ZeFj7oYLeysPG9Mv7Ibp7Jq5Wie1vLn3fX2ZSMEcs8aXDrSlx6Eso3TWiM+DJA3C9/EabxQqeXvyjcolok='; + + /** + * This is just a dump the localStorage `redux` object from the signed in sample account. + * Allows rapid login for this test suite. This will need updated occasionally after changes to + * this object schema. Does not include the lazy-loaded app list data. + */ + static LOCAL_STORAGE_DATA = "{\"account\":{\"accountCreated\":true,\"promptedForEmail\":false,\"email\":\"test_e2e_recovery@mail-apps.com\",\"encryptedBackupPhrase\":\"d91fa4ece7417c78f7ff22d4a93c519f0b6786e91348ec88b407dfb99a4463122d7049225bad239f39cb7720d21187a0631c6524b6077453bca6705d1f7a014e7b6c9f85474e5af104f3210681606f1d\",\"identityAccount\":{\"publicKeychain\":\"xpub6BD5AWkvt1ZPPvT7hbYw7mBU2fnsBZaw7q3ojAvnsGBPJLGLwuCJj7jLVi6XGhkz5cpLqRW9rnVBw1U41ckdZdS1bVYqTcnjKMUuRq45a88\",\"addresses\":[\"1NDsatzAEqrErxkB1osfJXouADgrHXuDs1\"],\"keypairs\":[{\"key\":\"2680e401fe670c15c43ad9686395177dea7a5f006848eb8eaf4729fc9ec289ee\",\"keyID\":\"03221818a0a3b3f9e3b0b369f085904c63c6caa506a3e1d28dcbc0d6a9aa7a82d2\",\"address\":\"1NDsatzAEqrErxkB1osfJXouADgrHXuDs1\",\"appsNodeKey\":\"xprvA2FnvytPSbUWyyrt3Bwxfaf6urjgAPHqaZZbNiXvE8HUZ4kRAvwNC1hkpjzsf1YnzR8e9SC8CXNBiLG4ZF1oPy3i5eVE5iEVU6YZVspN6tE\",\"salt\":\"19bc80bb27060f30fa4f05b07c92069c3633d6a70b089046f5605329fcdec798\"}],\"addressIndex\":1},\"bitcoinAccount\":{\"publicKeychain\":\"xpub6D1QcTvSqDXUsFF6n35HTohhgzov6LY9fRzNZF17MRTPzdvvN5NTtpUHPXujBEg1i1LFFcbdaFhiQiMgeWkjbCgZaAEB2cdcDxenpwnwpqd\",\"addresses\":[\"16ZsuXRZUipZHdrNPpkpDCJF8ZcwsssGbE\"],\"addressIndex\":0,\"balances\":{\"total\":0}},\"coreWallet\":{\"address\":null,\"balance\":0,\"withdrawal\":{\"txHex\":null,\"isBuilding\":false,\"isBroadcasting\":false,\"inProgress\":false,\"error\":null,\"recipientAddress\":null,\"success\":false}},\"viewedRecoveryCode\":false,\"recoveryCodeVerified\":false,\"connectedStorageAtLeastOnce\":true},\"auth\":{\"appManifest\":null,\"appManifestLoaded\":false,\"appManifestLoading\":false,\"appManifestLoadingError\":null,\"coreSessionTokens\":{},\"loggedIntoApp\":false},\"profiles\":{\"availability\":{\"names\":{},\"lastNameEntered\":null},\"identity\":{\"default\":0,\"localIdentities\":[{\"username\":\"test_e2e_recovery.id.blockstack\",\"usernameOwned\":true,\"usernamePending\":false,\"profile\":{\"@type\":\"Person\",\"@context\":\"http:\/\/schema.org\",\"api\":{\"gaiaHubConfig\":{\"url_prefix\":\"https:\/\/gaia.blockstack.org\/hub\/\"},\"gaiaHubUrl\":\"https:\/\/hub.blockstack.org\"},\"name\":\"Alice Devname\"},\"verifications\":[],\"trustLevel\":0,\"registered\":false,\"ownerAddress\":\"1NDsatzAEqrErxkB1osfJXouADgrHXuDs1\",\"zoneFile\":\"$ORIGIN test_e2e_recovery.id.blockstack\\n$TTL 3600\\n_http._tcp\\tIN\\tURI\\t10\\t1\\t\\\"https:\/\/gaia.blockstack.org\/hub\/1NDsatzAEqrErxkB1osfJXouADgrHXuDs1\/profile.json\\\"\\n\\n\"}],\"publicIdentities\":{},\"nameTransfers\":[],\"zoneFileUpdates\":[],\"createProfileError\":null,\"isProcessing\":false},\"pgp\":{\"publicKeys\":{}},\"registration\":{},\"search\":{\"query\":\"\",\"results\":[]}},\"sanity\":{\"coreApiRunning\":true,\"coreApiPasswordValid\":true},\"settings\":{\"api\":{\"apiCustomizationEnabled\":true,\"nameLookupUrl\":\"https:\/\/core.blockstack.org\/v1\/names\/{name}\",\"searchServiceUrl\":\"https:\/\/core.blockstack.org\/v1\/search?query={query}\",\"registerUrl\":\"https:\/\/core.blockstack.org\/v1\/names\",\"bitcoinAddressLookupUrl\":\"https:\/\/core.blockstack.org\/v1\/addresses\/bitcoin\/{address}\",\"zeroConfBalanceUrl\":\"https:\/\/core.blockstack.org\/v1\/wallet\/balance\/0\",\"insightUrl\":\"https:\/\/utxo.blockstack.org\/insight-api\/addr\/{address}\",\"btcBalanceUrl\":\"https:\/\/blockchain.info\/q\/addressbalance\/\",\"broadcastUrl\":\"https:\/\/utxo.blockstack.org\/insight-api\/tx\/send\",\"priceUrl\":\"https:\/\/core.blockstack.org\/v1\/prices\/names\/{name}\",\"networkFeeUrl\":\"https:\/\/bitcoinfees.21.co\/api\/v1\/fees\/recommended\",\"walletPaymentAddressUrl\":\"https:\/\/core.blockstack.org\/v1\/wallet\/payment_address\",\"pendingQueuesUrl\":\"https:\/\/core.blockstack.org\/v1\/blockchains\/bitcoin\/pending\",\"coreWalletWithdrawUrl\":\"https:\/\/core.blockstack.org\/v1\/wallet\/balance\",\"bitcoinAddressUrl\":\"https:\/\/explorer.blockstack.org\/address\/{identifier}\",\"ethereumAddressUrl\":\"https:\/\/tradeblock.com\/ethereum\/account\/{identifier}\",\"pgpKeyUrl\":\"https:\/\/pgp.mit.edu\/pks\/lookup?search={identifier}&op=vindex&fingerprint=on\",\"btcPriceUrl\":\"https:\/\/www.bitstamp.net\/api\/v2\/ticker\/btcusd\/?cors=1\",\"corePingUrl\":\"https:\/\/core.blockstack.org\/v1\/node\/ping\",\"zoneFileUrl\":\"https:\/\/core.blockstack.org\/v1\/names\/{name}\/zonefile\",\"nameTransferUrl\":\"https:\/\/core.blockstack.org\/v1\/names\/{name}\/owner\",\"subdomains\":{\"foo.id\":{\"registerUrl\":\"http:\/\/localhost:7103\/register\"},\"test-personal.id\":{\"registerUrl\":\"https:\/\/test-registrar.blockstack.org\/register\"},\"id.blockstack\":{\"registerUrl\":\"https:\/\/registrar.blockstack.org\/register\"}},\"browserServerUrl\":\"https:\/\/blockstack-browser-server.appartisan.com\",\"hostedDataLocation\":\"gaia-hub\",\"coreHost\":\"localhost\",\"corePort\":6270,\"coreAPIPassword\":\"PretendPasswordAPI\",\"logServerPort\":\"\",\"regTestMode\":false,\"storageConnected\":true,\"gaiaHubConfig\":{\"url_prefix\":\"https:\/\/gaia.blockstack.org\/hub\/\"},\"gaiaHubUrl\":\"https:\/\/hub.blockstack.org\",\"btcPrice\":\"1000.00\",\"distinctEventId\":\"063b145bc3f59dca962d585947f89d7a\",\"hasDisabledEventTracking\":false}},\"notifications\":[]}"; + + } \ No newline at end of file diff --git a/test-e2e/tsconfig.json b/test-e2e/tsconfig.json new file mode 100755 index 000000000..a35a8ee3a --- /dev/null +++ b/test-e2e/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compileOnSave": false, + "compilerOptions": { + "outDir": "./dist/out-tsc", + "baseUrl": "src", + "sourceMap": true, + "declaration": false, + "moduleResolution": "node", + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "target": "es5", + "typeRoots": [ + "node_modules/@types" + ], + "lib": [ + "es2016", + "dom" + ] + } +} diff --git a/test-e2e/tslint.json b/test-e2e/tslint.json new file mode 100755 index 000000000..2ba80f7a6 --- /dev/null +++ b/test-e2e/tslint.json @@ -0,0 +1,92 @@ +{ + "rules": { + "arrow-return-shorthand": true, + "callable-types": true, + "class-name": true, + "comment-format": [ + false, + "check-space" + ], + "curly": true, + "eofline": false, + "forin": true, + "import-blacklist": [ + true, + "rxjs" + ], + "import-spacing": true, + "indent": [ + true, + "spaces" + ], + "interface-over-type-literal": true, + "label-position": true, + "max-line-length": [ + false, + 140 + ], + "member-access": false, + "no-arg": true, + "no-bitwise": true, + "no-console": [ + true, + "debug", + "info", + "time", + "timeEnd", + "trace" + ], + "no-construct": true, + "no-debugger": true, + "no-duplicate-super": true, + "no-empty": false, + "no-empty-interface": true, + "no-eval": true, + "no-inferrable-types": [ + true, + "ignore-params" + ], + "no-misused-new": true, + "no-non-null-assertion": true, + "no-shadowed-variable": true, + "no-string-literal": false, + "no-string-throw": false, + "no-switch-case-fall-through": true, + "no-trailing-whitespace": false, + "no-unnecessary-initializer": true, + "no-unused-expression": true, + "no-use-before-declare": true, + "no-var-keyword": true, + "object-literal-sort-keys": false, + "one-line": [ + false, + "check-open-brace", + "check-catch", + "check-else", + "check-whitespace" + ], + "prefer-const": true, + "quotemark": [ + false, + "single" + ], + "radix": true, + "triple-equals": [ + false, + "allow-null-check" + ], + "typedef-whitespace": [ + false, + { + "call-signature": "nospace", + "index-signature": "nospace", + "parameter": "nospace", + "property-declaration": "nospace", + "variable-declaration": "nospace" + } + ], + "typeof-compare": true, + "unified-signatures": true, + "variable-name": false + } +} diff --git a/test-e2e/utils/ExtendedWebDriver.js b/test-e2e/utils/ExtendedWebDriver.js deleted file mode 100644 index 96720fd63..000000000 --- a/test-e2e/utils/ExtendedWebDriver.js +++ /dev/null @@ -1,267 +0,0 @@ -const { WebDriver, By, until, WebElement, logging } = require('selenium-webdriver'); -const { promisify } = require('util'); -const fs = require('fs'); -const os = require('os'); -const path = require('path'); -const helpers = require('./helpers'); - -/** - * Adds helper methods to a WebDriver instance. - * This includes some fairly typical boilerplate code - * for performing try/catch/retry techniques for reducing - * selenium flakiness - see http://lmgtfy.com/?q=selenium+flakiness - */ -class ExtendedWebDriver extends WebDriver { - - /** - * Uses a mixin pattern to merge the methods of a given WebDriver instance with - * this class instance. - * @param {WebDriver} webDriver - */ - constructor(driver) { - super(null, null); - this.driver = driver; - - // Merge functions from the given WebDriver instance into this instance. - helpers.mixin(this, driver); - } - - /** - * - * @param {string} script A javascript expression that evaluates to a promise. - * @param {...any} args - * @returns {any} The evaluated promise result, if any. - */ - async executePromise(script, ...args) { - const [error, result] = await this.driver.executeAsyncScript(` - var callback = arguments[arguments.length - 1]; - ${script} - .then(result => callback([null, result])) - .catch(error => callback([error.toString(), null])); - `, ...args); - if (error) { - throw new Error(error); - } - return result; - } - - /** - * Retries a given function until the given time has elapsed. - * @param {(Promise|(function():any)} func - * @param {number} timeout - milliseconds to continue re-trying execution of a failing function. - * @param {number} poll - milliseconds to wait before trying a failed function. - */ - async retry(func, timeout, poll) { - const startTime = Date.now(); - let lastErr; - const hasTimeRemaining = () => (Date.now() - startTime < timeout); - while (hasTimeRemaining()) { - try { - return await Promise.resolve(func()); - } catch (err) { - lastErr = err; - if (hasTimeRemaining()) { - await this.driver.sleep(poll); - if (hasTimeRemaining()) { - console.warn(`Retrying after getting error: ${err}`); - continue; - } - } - break; - } - } - console.error(`Error after timeout period has elapsed.`); - throw lastErr; - } - - async getPlatform() { - if (this.platform) { - return this.platform; - } - const session = await this.getSession(); - const platform = session.getCapabilities().getPlatform(); - this.platform = platform; - return platform; - } - - async getSessionID() { - const session = await this.getSession(); - return session.getId(); - } - - /** - * This uses Selenium's `manage().logs()` API which is now only supported by Chrome's - * WebDriver. Some remote WebDriver services (like BrowserStack) shim support for - * this API on their end for some web browsers. Expect this to throw various kinds of - * Errors, or silently failing with an empty result - depending on the environment and browser. - */ - async getBrowserLogs() { - const logEntries = await this.driver.manage().logs().get(logging.Type.BROWSER); - if (!logEntries) { - throw new Error('Not supported - falsy `logs` object returned'); - } - const logs = logEntries.map(entry => entry.toJSON()); - const logJson = JSON.stringify(logs, null, 2); - return logJson; - } - - /** - * Loops with try/catch around the locator function for specified amount of tries. - * @param {!(By|Function)} locator The locator to use. - * @param {string} text The string of keys to send. - * @param {boolean} validateValue - * If true then the element's value will be checked after a short wait to ensure it was set. - * If validation fails it will attempted one more time. - * @returns {Promise} - */ - async setText(locator, text, validateValue = false) { - const platform = await this.getPlatform(); - let element; - if (platform !== 'iOS') { - element = await this.el(locator, async (el) => { - await el.clear(); - await el.sendKeys(text); - }); - } else { - // Bug on iOS devices with selenium/appium & react - // See: https://github.com/facebook/react/issues/11488#issuecomment-347775628 - // Updated to use less hacky workaround from cypress: - // See: https://github.com/cypress-io/cypress/pull/732/files#diff-ed17d49edc10403752ba3d786a7512dbR34 - element = await this.el(locator, async (el) => { - await this.executeScript(` - let input = arguments[0]; - if (input.tagName === "INPUT") { - let valueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value").set; - valueSetter.call(input, arguments[1]); - } else { - let valueSetter = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, "value").set; - valueSetter.call(input, arguments[1]); - } - input.dispatchEvent(new Event('input', { bubbles: true })); - `, el, text); - }); - } - - if (validateValue) { - try { - await this.sleep(200); - let actualVal = ''; - const elCheck = await this.el(locator, async (el) => { - actualVal = await this.executeScript(`return arguments[0].value;`, el); - }); - if (actualVal !== text) { - console.warn(`setText value validation failed - expected '${text}' but value is '${actualVal}'. Retrying once more...`); - try { - await elCheck.clear(); - } catch (error) { - console.warn(`Ignoring error trying to clear element value before retrying: ${error}`); - } - return await this.setText(locator, text); - } - } catch (error) { - console.error(`Error validating setText value: ${error}`); - throw error; - } - } - - return element; - } - - /** - * @param {WebElement} element - */ - async scrollIntoView(element) { - await this.driver.executeScript('arguments[0].scrollIntoView(true);', element); - } - - /** - * @callback elementThenCallback - * @param {WebElement} element - */ - - /** - * Waits until the element is both located and visible, and scrolls the element into view. - * @param {!(By|Function)} locator The locator to use. - * @param {elementThenCallback} then - * @returns {Promise} - */ - async el(locator, then = undefined, { timeout = 15000, poll = 200, driverWait = 2500 } = {}) { - return await this.retry(async () => { - let el = await this.driver.wait(until.elementLocated(locator), driverWait); - await this.driver.wait(until.elementIsVisible(el), driverWait); - await this.scrollIntoView(el); - if (then) { - await Promise.resolve(then(el)); - } - return el; - }, timeout, poll); - } - - /** - * Waits for the element to be located. - * @param {!(By|Function)} locator The locator to use. - * @param {elementThenCallback} then - * @returns {Promise} - */ - async waitForElementLocated(locator, { timeout = 15000, poll = 200, driverWait = 2500 } = {}) { - return await this.retry(async () => { - return await this.driver.wait(until.elementLocated(locator), driverWait); - }, timeout, poll); - } - - /** - * Loops with try/catch around the locator and click function for specified amount of tries. - * @param {!(By|Function)} locator The locator to use. - * @returns {Promise} - */ - async click(locator, { timeout = 15000, poll = 200, driverWait = 2500 } = {}) { - return await this.el(locator, el => el.click(), { timeout, poll, driverWait }); - } - - /** - * @param {!(By|Function)} locator The locator to use. - */ - async elementExists(locator) { - const matches = await this.findElements(locator); - return matches.length > 0; - } - - /** - * @param {!(By|Function)} locator The locator to use. - */ - async elementNotExists(locator) { - const matches = await this.findElements(locator); - return matches.length > 0; - } - - async screenshot(filename = '') { - if (!filename) { - filename = `screenshot-${Date.now()/1000|0}-${helpers.getRandomString(6)}.png` - filename = path.resolve(os.tmpdir(), filename); - } - const image = await this.driver.takeScreenshot(); - await promisify(fs.writeFile)(filename, image, 'base64'); - return filename; - } - - /** - * @param {string} url - */ - async get(url, retries = 3) { - for (let i = 0; i < retries; i++) { - try { - await this.driver.get(url); - return; - } catch (err) { - if (i === retries - 1) { - console.error(`Error loading initial page: ${err}`) - throw err; - } - await this.driver.sleep(200); - console.warn('Retrying after error loading initial page...'); - } - } - } -} - -module.exports = ExtendedWebDriver; diff --git a/test-e2e/utils/browserstack-api.js b/test-e2e/utils/browserstack-api.js deleted file mode 100644 index 47432789b..000000000 --- a/test-e2e/utils/browserstack-api.js +++ /dev/null @@ -1,68 +0,0 @@ -const { promisify } = require('util'); -const request = promisify(require('request')); -const config = require('./config'); - -/** - * Uses a legacy BrowserStack rest API endpoint to retrieve the console logs for a given session. - * This method doesn't need the buildID and so doesn't require the hacky regex required to extract it. - * However, the log endpoint it uses is deprecated and probably won't work in the future. - * @param {string} sessionID The WebDriver Session ID. - * @returns {Promise} The console log file dump. - */ -async function getSessionConsoleLogsLegacy(sessionID) { - try { - const sessionInfoUrl = `https://api.browserstack.com/automate/sessions/${sessionID}.json`; - const sessionInfoRequest = await request({ - url: sessionInfoUrl, - auth: { user: config.browserStack.user, pass: config.browserStack.key, sendImmediately: true }, - followRedirect: false, - }); - const sessionInfo = JSON.parse(sessionInfoRequest.body).automation_session; - const consoleLogUrl = sessionInfo.browser_console_logs_url; - const logRequest = await request({ - url: consoleLogUrl, - followRedirect: false, - }); - if (logRequest.statusCode !== 200) { - throw new Error(`${logRequest.statusCode}: ${logRequest.statusMessage}`); - } - const logBody = logRequest.body; - return logBody; - } catch (error) { - console.log(`Error getting BrowserStack session console.log: ${error}`); - } -} - -/** - * Uses the BrowserStack rest API to retrieve the console logs for a given session. - * @param {string} sessionID The WebDriver Session ID. - * @returns {Promise} The console log file dump. - */ -async function getSessionConsoleLogs(sessionID) { - const sessionInfoUrl = `https://api.browserstack.com/automate/sessions/${sessionID}.json`; - const sessionInfoRequest = await request({ - url: sessionInfoUrl, - auth: { user: config.browserStack.user, pass: config.browserStack.key, sendImmediately: true }, - followRedirect: false - }); - const sessionInfo = JSON.parse(sessionInfoRequest.body).automation_session; - // This seems to be the best way to determine the BuildID - const buildID = sessionInfo.logs.match("builds/([a-z0-9]+)/sessions")[1]; - const consoleLogUrl = `https://api.browserstack.com/automate/builds/${buildID}/sessions/${sessionID}/consolelogs`; - const logRequest = await request({ - url: consoleLogUrl, - auth: { user: config.browserStack.user, pass: config.browserStack.key, sendImmediately: true }, - followRedirect: false, - headers: { - // Required by BrowserStack's API server.. - 'Accept': '*/*', - } - }); - if (logRequest.statusCode !== 200) { - throw new Error(`${logRequest.statusCode}: ${logRequest.statusMessage}`); - } - const logBody = logRequest.body; - return logBody; -} - -module.exports = { getSessionConsoleLogs, getSessionConsoleLogsLegacy }; diff --git a/test-e2e/utils/can-open-protocol.js b/test-e2e/utils/can-open-protocol.js deleted file mode 100644 index 166dc0659..000000000 --- a/test-e2e/utils/can-open-protocol.js +++ /dev/null @@ -1,73 +0,0 @@ -const { execSync } = require('child_process'); - -/** @type {boolean} */ -let cachedValue = undefined; - -/** - * Queries the local system for the ability to open the `blockstack:` protocol. - * As in, determines if the native Blockstack Browser app is installed. - * Note: Currently only supported on MacOS. Always returns false on other systems. - * @returns {boolean} Returns true if the protocol handler is registered. - */ -function canOpenProtocol() { - if (cachedValue !== undefined) { - return cachedValue; - } - return cachedValue = (() => { - try { - switch (process.platform) { - case 'darwin': return canOpenProtocolDarwin(); - default: return false; - } - } catch (error) { - console.log(`Error trying to detect protocol handler: ${error}`); - return false; - } - })(); -} - -function canOpenProtocolDarwin() { - - /** - * For more details.. - * @see https://superuser.com/a/413606 - * @see https://github.com/nwjs/nw.js/issues/951#issuecomment-130117544 - */ - const launchServicesDir = '/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support'; - - const lsregisterDump = () => execSync(`${launchServicesDir}/lsregister -dump`, { encoding: 'utf8' }); - - const checkIsHandlerRegistered = () => { - const stdout = lsregisterDump(); - const hasHandler = /bindings:\s+blockstack:/.test(stdout); - return hasHandler; - }; - - const clearRegisteredHandlers = () => { - const stdout = lsregisterDump(); - const pathRegex = /path:\s+(.*?)\/Blockstack.app\n/g; - let match = null; - while (match = pathRegex.exec(stdout)) { - const appPath = `${match[1]}/Blockstack.app` - execSync(`${launchServicesDir}/lsregister -u "${appPath}"`, { encoding: 'utf8' }); - } - }; - - const isHandlerRegistered = checkIsHandlerRegistered(); - if (isHandlerRegistered) { - // Try clearing out the handler registrations - console.warn('Notice: Clearing the native blockstack protocol handler from the local system. To restore, just re-open Blockstack.app'); - clearRegisteredHandlers(); - // Then check again and return the result. - const isStillRegistered = checkIsHandlerRegistered(); - if (isStillRegistered) { - console.warn('Was unable to clear the native protocol handler registration from the system.') - } - return isStillRegistered; - } else { - return false; - } - -} - -module.exports = canOpenProtocol; diff --git a/test-e2e/utils/config.js b/test-e2e/utils/config.js deleted file mode 100644 index 43f28103e..000000000 --- a/test-e2e/utils/config.js +++ /dev/null @@ -1,119 +0,0 @@ -const path = require('path'); -const url = require('url'); -const helpers = require('./helpers'); - -const BROWSERSTACK_LOOPBACK_HOST = 'bs-local.com'; -const BROWSERSTACK_HUB_URL = 'http://hub-cloud.browserstack.com/wd/hub'; - -const config = { - browserHostUrl: '', - browserStack: { - enabled: false, - user: '', - key: '', - localEnabled: false, - localIdentifier: '', - hubUrl: '' - }, - serveDirectory: '', - loopbackHost: 'localhost' -}; - -/** - * Note: This config has to be loaded immediately and synchronously since the config values - * are used for generating the Mocha test suites, and Mocha requires tests to be defined - * immediately and synchronously on script load. - */ -(function initializeConfig() { - - // Determine which browser host endpoint to run tests against. - const E2E_BROWSER_HOST = 'E2E_BROWSER_HOST'; - const PROD_HOST = 'https://browser.blockstack.org'; - config.browserHostUrl = process.env[E2E_BROWSER_HOST] || PROD_HOST; - if (!process.env[E2E_BROWSER_HOST]) { - console.warn(`WARNING: The browser host url was not set via the ${E2E_BROWSER_HOST} env var.. running tests against the production endpoint "${PROD_HOST}"`); - } else if (config.browserHostUrl.startsWith('http:') || config.browserHostUrl.startsWith('https:')) { - console.log(`Running e2e tests against endpoint ${config.browserHostUrl}`); - } else { - config.serveDirectory = path.resolve(config.browserHostUrl); - config.browserHostUrl = 'http://localhost:5757'; - console.log(`Local static web server will be started at ${config.browserHostUrl} for directory ${config.serveDirectory}`); - } - - // Check environment vars for BrowserStack usage settings. - const USE_BROWSERSTACK = 'USE_BROWSERSTACK'; - const BROWSERSTACK_AUTH = 'BROWSERSTACK_AUTH'; - config.browserStack.enabled = !helpers.isFalsy(process.env[USE_BROWSERSTACK]); - if (config.browserStack.enabled) { - config.browserStack.hubUrl = BROWSERSTACK_HUB_URL; - const browserstackAuth = process.env[BROWSERSTACK_AUTH]; - if (!browserstackAuth) { - const errMsg = `The BrowserStack auth must be set as environment variables. Use the format \`${BROWSERSTACK_AUTH}="user:key"\``; - console.error(errMsg); - throw new Error(errMsg); - } - // Auth string formatted as "user:key" - [config.browserStack.user, config.browserStack.key] = browserstackAuth.trim().split(/:(.+)/); - } - - /** - * If the auth-browser host endpoint is set to localhost and BrowserStack testing is enabled - * then BrowserStack Local must be used. - * @see https://www.npmjs.com/package/browserstack-local - * @see https://www.browserstack.com/local-testing - */ - if (config.browserStack.enabled) { - const parsedUrl = url.parse(config.browserHostUrl); - config.browserStack.localEnabled = ['localhost', '127.0.0.1'].includes(parsedUrl.hostname); - config.browserStack.localIdentifier = helpers.getRandomString(20); - - /** - * Check if the host port is the expected port that is supported by BrowserStack Safari environments. - * @see https://www.browserstack.com/question/664 - */ - const expectedPort = '5757'; - if (config.browserStack.localEnabled && parsedUrl.port !== expectedPort) { - console.warn(`WARNING: BrowserStack Local is enabled but the host port is ${parsedUrl.port} rather than the expected port ${expectedPort}. ` + - `This may cause problems for BrowserStack Safari environments.. for more information see https://www.browserstack.com/question/664`); - } - } - - /** - * If BrowserStack is enabled, then include their 'fast-selenium.js' script. - * @see https://www.browserstack.com/automate/node#add-on - * @see https://raw.githubusercontent.com/browserstack/fast-selenium-scripts/master/node/fast-selenium.js - */ - if (config.browserStack.enabled) { - require('./fast-selenium'); - } - - /** - * If BrowserStack Local is enabled then the host url needs swapped from localhost to bs-local.com - * This required due to a technical limitation with BrowserStack's Safari environments. - * @see https://www.browserstack.com/question/759 - */ - if (config.browserStack.localEnabled) { - const parsedUrl = url.parse(config.browserHostUrl); - [ parsedUrl.hostname, parsedUrl.host ] = [ BROWSERSTACK_LOOPBACK_HOST, undefined ]; - config.browserHostUrl = url.format(parsedUrl); - config.loopbackHost = BROWSERSTACK_LOOPBACK_HOST; - } - - // Trim trailing url slash(es). - config.browserHostUrl = config.browserHostUrl.replace(/\/+$/, ""); - - return config; - -})(); - - -// Prevent the error message -// "MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 exit listeners added. Use emitter.setMaxListeners() to increase limit" -// We don't care about this.. -// https://github.com/SeleniumHQ/selenium/issues/6812 -// https://github.com/nightwatchjs/nightwatch/issues/408 -process.setMaxListeners(0); -require('events').EventEmitter.defaultMaxListeners = 1000; - - -module.exports = config; diff --git a/test-e2e/utils/create-test-suites.js b/test-e2e/utils/create-test-suites.js deleted file mode 100644 index 9d40ff73d..000000000 --- a/test-e2e/utils/create-test-suites.js +++ /dev/null @@ -1,259 +0,0 @@ - -const { WebDriver, Builder, By, Key, until, logging } = require('selenium-webdriver'); -const path = require('path'); -const fs = require('fs'); -const os = require('os'); -const url = require('url'); -const filenamify = require('filenamify'); -const { createServer: createHttpServer, Server: HttpServer } = require('http'); -const serveHandler = require('serve-handler'); -const { Local: BrowserStackLocal } = require('browserstack-local'); -const ExtendedWebDriver = require('./ExtendedWebDriver'); -const helpers = require('./helpers'); -const config = require('./config'); -const { getTestEnvironments } = require('./webdriver-environments'); -const { getSessionConsoleLogs } = require('./browserstack-api'); - - -// selenium-webdriver docs: https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/webdriver.html - - -/** @type {BrowserStackLocal} */ -let browserStackLocalInstance; - -/** @type {HttpServer} */ -let staticWebServer; - -before(async () => { - - // Check if a local static web server needs to be started - if (config.serveDirectory) { - console.log(`Starting static web server for a directory to host the Browser locally...`) - staticWebServer = createHttpServer((req, res) => { - return serveHandler(req, res, { - public: config.serveDirectory, - rewrites: [{ source: '**', destination: '/index.html' }] - }) - }); - await new Promise((resolve, reject) => { - staticWebServer.unref(); - staticWebServer.listen(url.parse(config.browserHostUrl).port, error => { - if (error) { - console.error(`Error starting web server: ${error}`); - reject(error); - } else { - console.log(`Web server started at http://localhost:${staticWebServer.address().port}`); - resolve(); - } - }); - }); - } - - // Check if BrowserStackLocal needs to be initialized before running tests.. - if (config.browserStack.localEnabled) { - console.log(`BrowserStack is enabled the test endpoint is localhost, setting up BrowserStack Local..`); - browserStackLocalInstance = new BrowserStackLocal(); - await new Promise((resolve, reject) => { - const opts = { - key: config.browserStack.key, - force: 'true', - localIdentifier: config.browserStack.localIdentifier - }; - browserStackLocalInstance.start(opts, (error) => { - if (error) { - console.error(`Error starting BrowserStack Local: ${error}`); - reject(error) - } else { - console.log(`BrowserStack Local started`); - resolve(); - } - }); - }); - } - -}); - -after(async () => { - - // Check if a local web server needs to be shutdown - if (staticWebServer && staticWebServer.listening) { - await new Promise((resolve) => { - staticWebServer.close(error => { - if (error) { - console.error(`Error stopping local static web server: ${error}`); - } - resolve(); - }); - }); - } - - // Check if BrowserStackLocal needs to be disposed off after running tests.. - if (browserStackLocalInstance && browserStackLocalInstance.isRunning()) { - await new Promise((resolve) => { - browserStackLocalInstance.stop((error) => { - if (error) { - console.error(`Error stopping BrowserStack Local: ${error}`); - } - resolve(); - }); - }); - } - -}); - - - -/** - * @typedef {Object} TestInputs - * @property {ExtendedWebDriver} driver A ready to use WebDriver instance. - * @property {string} browserHostUrl The http endpoint hosting the browser. - * @property {string} envDesc Human-readable name of the operating system & web browser. - * @property {string} browserName Lowercase name of web browser (for mobile this can be 'android' or 'iphone'). - * @property {boolean} browserStackEnabled If testing against BrowserStack is enabled. - * @property {string} loopbackHost Typically `localhost`, otherwise set to BrowserStack's loopback domain. - */ - -/** - * @callback DefineTestsCallback - * @param {TestInputs} testInputs - * @returns {void} - */ - -/** - * @param {string} title Test suite title used in the `describe` statement. - * @param {DefineTestsCallback} defineTests - * Callback that is invoked for each test environment. - * Mocha test (`it`, `step`, etc) should be defined inside in this callback. - * Any test failures automatically trigger a screenshot that is written to file. - * The WebDriver instance is automatically disposed/quitted at the end of the test suite. - */ -function createTestSuites(title, defineTests) { - - const testEnvironments = getTestEnvironments(); - for (const testEnvironment of testEnvironments) { - - describe(`${title} [${testEnvironment.description}]`, function() { - - if (config.browserStack.enabled) { - this.retries(1); - } - - /** @type {TestInputs} */ - const testInputs = { - envDesc: testEnvironment.description, - browserName: testEnvironment.browserName, - browserHostUrl: config.browserHostUrl, - browserStackEnabled: config.browserStack.enabled, - loopbackHost: config.loopbackHost, - driver: {}, - driverInitialized: false - }; - - // Variables that get populated when a test fails - const failedTestInfo = { - sessionID: '', - testFileName: '', - fileDir: '' - }; - - step('create selenium webdriver', async () => { - // BrowserStack sometimes lets webdriver instantiation network requests go into a zombie - // state (no response, no error) after several minutes, causing a Mocha test timeout - // with cascading effects that prevent the `retries` feature from working properly. - // There is no easy way to set a timeout or cancel this webdriver network request, so we - // use a manual Promise race to detect timeout and fail the test. - const createDriver = async () => { - const driver = await testEnvironment.createDriver(); - helpers.mixin(testInputs.driver, driver); - testInputs.driverInitialized = true; - }; - const timeout = new Promise((resolve, reject) => { - setTimeout(() => { - testInputs.driverInitialized - ? resolve() - : reject(new Error('Timeout waiting for webdriver instantiation')); - }, 60000).unref(); - }); - await Promise.race([createDriver(), timeout]); - }); - - defineTests(testInputs) - - afterEach(function () { - - // If test failed then prepare for retrieving debug data (logs, screenshots). - if (this.currentTest.state === 'failed' && testInputs.driverInitialized) { - - // Setup temp dir to write debug data, with a filename-safe description of the failed test. - const testDesc = this.currentTest.titlePath().join('-').replace(/\s+/g, '-'); - const testFileName = filenamify(`${testDesc}-${Date.now()/10000|0}-${helpers.getRandomString(5)}`); - const fileDir = path.resolve(os.tmpdir(), 'test-errors'); - fs.mkdirSync(fileDir, { recursive: true }); - - failedTestInfo.testFileName = testFileName; - failedTestInfo.fileDir = fileDir; - - return Promise.resolve().then(async function getSessionID() { - try { - if (config.browserStack.enabled) { - // If BrowserStack is enabled, get the sessionID which we need to retrieve the logs - // after the session has been closed. - failedTestInfo.sessionID = await testInputs.driver.getSessionID(); - } - } catch (error) { - console.warn(`Error trying to get sessionID after test failure: ${error}`); - } - }).then(async function createScreenshotFile() { - try { - const screenshotFile = path.resolve(fileDir, `${testFileName}.png`); - await testInputs.driver.screenshot(screenshotFile); - console.log(`screenshot saved to "${screenshotFile}"`); - } catch (error) { - console.warn(`Error trying to create screenshot after test failure: ${error}`); - } - }).then(async function getSeleniumBrowserLogs(){ - try { - const logData = await testInputs.driver.getBrowserLogs(); - const logFileDir = path.resolve(fileDir, `${testFileName}.selenium.log.txt`); - fs.writeFileSync(logFileDir, logData); - console.log(`selenium logs saved to "${logFileDir}"`); - } catch (error) { - console.warn(`Selenium log API not supported for this environment: ${error}`); - } - }); - } - - }); - - after(async () => { - - try { - if (testInputs.driverInitialized) { - await testInputs.driver.quit(); - } - } catch (error) { - console.warn(`Error disposing driver after tests: ${error}`); - } - - try { - if (config.browserStack.enabled && failedTestInfo.sessionID) { - // This must be ran after `driver.quit()` since BrowserStack logs are not available - // through their API until after the session has ended. - // Wait for BrowserStack backend to propagate log data before requesting.. - await helpers.timeout(2500); - const logs = await getSessionConsoleLogs(failedTestInfo.sessionID); - const logFilePath = path.resolve(failedTestInfo.fileDir, `${failedTestInfo.testFileName}.browserstack.log.txt`); - fs.writeFileSync(logFilePath, logs); - console.log(`BrowserStack log data saved to "${logFilePath}"`); - } - } catch (error) { - console.warn(`Error fetching BrowserStack console logs: ${error}`); - } - - }); - - }); - } -} - -module.exports = createTestSuites; diff --git a/test-e2e/utils/webdriver-environments.js b/test-e2e/utils/webdriver-environments.js deleted file mode 100644 index 3cb4012ef..000000000 --- a/test-e2e/utils/webdriver-environments.js +++ /dev/null @@ -1,138 +0,0 @@ -const { WebDriver, Builder, By, Key, until, logging, Capabilities, Capability } = require('selenium-webdriver'); -const chromeOptions = require('selenium-webdriver/chrome').Options; -const firefoxOptions = require('selenium-webdriver/firefox').Options; -const path = require('path'); -const fs = require('fs'); -const os = require('os'); -const ExtendedWebDriver = require('./ExtendedWebDriver'); -const browserStackEnvironments = require('./browserstack-environments'); -const helpers = require('./helpers'); -const config = require('./config'); - -function getLoggingPrefs() { - const prefs = new logging.Preferences(); - prefs.setLevel(logging.Type.BROWSER, logging.Level.ALL); - return prefs; -}; - -/** - * @typedef {Object} TestEnvironment - * @property {string} description Human-readable name of the operating system & web browser. - * @property {string} browserName Lowercase name of web browser (for mobile this can be 'android' or 'iphone'). - * @property {Promise} createDriver Promise that resolves to a ready-to-use WebDriver instance. - */ - -/** - * @generator - * @param {string} user BrowserStack user credential. - * @param {string} key BrowserStack key credential. - * @yields {TestEnvironment} - */ -function* getBrowserstackEnvironments(user, key) { - for (const env of browserStackEnvironments) { - const caps = new Capabilities(env).merge({ - 'browserstack.user': user, - 'browserstack.key': key, - 'browserstack.console': 'verbose' - }); - - // Increase the `driver.executeAsyncScript(...)` timeout from zero to something usable. - // https://stackoverflow.com/a/31121340/794962 - const scriptTimeout = 60000; - caps.merge({'timeouts': { script: scriptTimeout }}).set(Capability.TIMEOUTS, { script: scriptTimeout }); - - if (config.browserStack.localEnabled) { - caps.merge({ - 'browserstack.local': 'true', - 'browserstack.localIdentifier': config.browserStack.localIdentifier - }); - } - - yield { - description: env.desc, - browserName: env.browserName.toLowerCase(), - createDriver: async () => { - const driver = await new Builder() - .usingServer(config.browserStack.hubUrl) - .withCapabilities(caps) - .setLoggingPrefs(getLoggingPrefs()) - .build(); - - // iOS on BrowserStack seems to ignore script timeouts specified in - // the Capabilities so explicitly set them here. - await driver.manage().setTimeouts({ script: scriptTimeout }); - - return new ExtendedWebDriver(driver); - } - }; - } -} - -/** - * Generates test environments for the local machine. Always includes 'chrome' and 'firefox'. - * If on macOS then also includes 'safari'. If on Windows then also includes 'edge'. - * @generator - * @yields {TestEnvironment} - */ -function* getLocalSystemBrowserEnvironments() { - const browsers = ['firefox', 'chrome']; - - // Ensure the browser webdriver binaries are added to env path - require('chromedriver'); - require('geckodriver'); - - if (process.platform === 'darwin') { - browsers.push('safari'); - } else if (process.platform === 'win32') { - browsers.push('edge'); - } - - // Disable Chrome's protocol handler for `blockstack:` in case the native browser is installed on this machine - // https://stackoverflow.com/a/41299296/794962 - // Note: This ability has since been disabled by Chrome, there is no way to hide the prompt. - // https://github.com/chromium/chromium/blob/5f0fb8c9021d25d1fadc1ae3706b4790dbcded5a/chrome/browser/external_protocol/external_protocol_handler.cc#L194 - const chromeOpts = new chromeOptions() - .setUserPreferences({protocol_handler: { excluded_schemes: { 'blockstack': true } } }); - - // Disable Firefox's protocol handler for `blockstack:` in case the native browser is installed on this machine - // https://stackoverflow.com/a/53154527/794962 - const firefoxOpts = (() => { - const handlers = '{"defaultHandlersVersion":{"en-US":4},"schemes":{"blockstack":{"action":2,"handlers":[{"name":"None","uriTemplate":"#"}]}}}'; - const tempDir = path.resolve(os.tmpdir(), helpers.getRandomString()); - fs.mkdirSync(tempDir); - fs.writeFileSync(path.resolve(tempDir, 'handlers.json'), handlers); - return new firefoxOptions().setProfile(tempDir); - })(); - - const caps = new Capabilities().setLoggingPrefs(getLoggingPrefs()); - - // Increase the `driver.executeAsyncScript(...)` timeout from zero to something usable. - // https://stackoverflow.com/a/31121340/794962 - const scriptTimeout = 60000; - caps.merge({ 'timeouts': { script: scriptTimeout } }).set(Capability.TIMEOUTS, { script: scriptTimeout }); - - for (let browser of new Set(browsers)) { - yield { - description: `${process.platform} ${browser}`, - browserName: browser.toLowerCase(), - createDriver: async () => { - const driver = await new Builder() - .withCapabilities(caps) - .forBrowser(browser) - .setChromeOptions(chromeOpts) - .setFirefoxOptions(firefoxOpts) - .build(); - return new ExtendedWebDriver(driver); - } - }; - } -} - - -module.exports.getTestEnvironments = function () { - if (config.browserStack.enabled) { - return getBrowserstackEnvironments(config.browserStack.user, config.browserStack.key); - } else { - return getLocalSystemBrowserEnvironments(); - } -};