diff --git a/test/.gitignore b/test/.gitignore new file mode 100644 index 000000000..d6433b180 --- /dev/null +++ b/test/.gitignore @@ -0,0 +1,6 @@ +.github-token +bin/*.js +bin/visreg.config.ts +built/ +index.js +screenshots diff --git a/test/bin/selenium/.gitignore b/test/bin/selenium/.gitignore new file mode 100644 index 000000000..c96a04f00 --- /dev/null +++ b/test/bin/selenium/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/test/bin/util.ts b/test/bin/util.ts new file mode 100644 index 000000000..5a359e5f4 --- /dev/null +++ b/test/bin/util.ts @@ -0,0 +1,277 @@ +/* + * Utility functions to de-clutter `visreg.ts`. + */ +import * as _ from "lodash"; +import * as child_process from "child_process"; +import * as fs from "fs"; +import * as https from "https"; +import * as path from "path"; +import * as url from "url"; + +import { + IOptions, + input, + password, +} from "inquirer-shortcuts" +import * as opn from "opn" + +import {config, IConfig} from "./visreg.config"; + +export const f = "./.github-token"; +export const repoUrl = url.parse(`https://${config.githubHostname}/${config.githubName}/${config.repo}`); +export const hasCommitRegex = /\[.*([0-9a-f]{7})] Checking in screenshots.../; + +export async function checkConfig() { + const javascriptConfig = "./built/bin/visreg.config.js"; + const typescriptConfig = "./bin/visreg.config.ts"; + + if (fs.existsSync(javascriptConfig)) { + const dataJs = fs.readFileSync(javascriptConfig).toString(); + const updateJs = /name1234/.test(dataJs); + + const dataTs = fs.readFileSync(typescriptConfig).toString(); + const updateTs = /name1234/.test(dataTs); + + if (updateJs || updateTs) { + const defaultUsername = "your sso"; + const options: IOptions = { + default: defaultUsername, + message: "Github Enterprise username?", + }; + + const name = await input(options.message, options) as string; + if (name === defaultUsername) { + console.log(`Visit ${config.githubHostname} and log in to make sure this works.`); + console.log("After logging in, copy the last part of the url on your homepage. It's likely your sso."); + throw new Error("Please enter a valid Github Enterprise username."); + } + + const js = dataJs.replace(/name1234/, name); + updateJs && fs.writeFileSync(javascriptConfig, js); + + const ts = dataTs.replace(/name1234/, name); + updateTs && fs.writeFileSync(typescriptConfig, ts); + } + } + +} + +export async function getBranchName(targetBranch: string) { + const options: IOptions = { + default: "master", + message: "Baseline branch?", + }; + + const branch = targetBranch || await input(options.message, options) as string; + return branch; +} + +export async function getGithubToken() { + const storedToken: any = fs.existsSync(f) && fs.readFileSync(f); + const token = (storedToken || (await password("github PAT"))); + + return token; +} + +export async function validateToken(token: string) { + process.stdout.write("Checking github enterprise PAT..."); + if (!isValidToken(token)) { + console.log(" ✘"); + opn("https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/"); + throw new Error("Invalid Token"); + } + + fs.writeFileSync(f, token); + console.log(" ✔"); + + return token; +} + +export function cmd(command: string) { + child_process.execSync(`${command}`, { stdio: [0, 1, 2] }) +}; + +export function createBaseline( + token: string, + branch: string, + screenshotsDirectory: string, + targetBranch: string +) { + cmd([ + `cd ${screenshotsDirectory}`, + `git checkout -b ${branch}`, + "find . -maxdepth 1 -mindepth 1 -type d | grep -v \"./\.git\" | xargs rm -rf", + "git commit --allow-empty -m \"Baseline.\"", + "cd -" + ].join('; ')); + cmd(`git checkout ${targetBranch}; npm test`); + + const commit = commitScreenshots(token, screenshotsDirectory, "before").toString(); + const baseCommitMatch = commit.match(hasCommitRegex); + const baseCommit = baseCommitMatch && baseCommitMatch[1]; + + if (!baseCommit) { + throw new Error("Malformed baseline. Try again with a clean working state, and double check your branches.."); + } + + return baseCommit; +} + +export function createDiff(token: string, currentBranch: string, screenshotsDirectory: string) { + cmd(`git checkout ${currentBranch}; npm test`); + const afterCommitData = commitScreenshots(token, screenshotsDirectory, "after").toString(); + const afterCommitMatch = afterCommitData.match(hasCommitRegex); + const afterCommit = afterCommitMatch && afterCommitMatch[1]; + + const unknownError = (!afterCommit && !(/nothing to commit, working tree clean/).test(afterCommitData)); + const nothingToCommit = (!afterCommit && (/nothing to commit, working tree clean/).test(afterCommitData)); + if (nothingToCommit) { + console.log(afterCommitData); + process.exit(0); + } + + if (unknownError) { + throw new Error("Something has gone very wrong " + afterCommitData); + } + + return afterCommit; +} + +export function pushBranch(token: string, repoUrl: url.Url, branch: string, screenshotsDirectory: string) { + let pushUrl = `https://${token}@${repoUrl.hostname}/${config.githubName}/${config.repo}.git`; + console.log(`Generating remote screenshot diff...`); + safeExecSync(token, `cd ${screenshotsDirectory}; git push ${pushUrl} ${branch} > /dev/null 2>&1`); +}; + + +export function buildApiUrl(repoUrl: url.Url, resource: string) { + if (repoUrl.hostname !== "github.com") { + // github enterprise + return url.parse(`https://${repoUrl.hostname}/api/v3${resource}`); + } + + return url.parse(`https://api.${repoUrl.hostname}${resource}`); +}; + + +export function safeExecSync(token: string, command: string) { + try { + return child_process.execSync(command); + } catch (e) { + e.message = e.message.replace(token, `${token.slice(0, 4)}....${token.slice(-4)}`); + e.stack = e.stack.replace(token, `${token.slice(0, 4)}....${token.slice(-4)}`); + throw e; + } +}; + +export function buildCurlFlags(token: string) { + const flags = [ + `-H "Authorization: token ${token}"`, + '-H "User-Agent: snappit"' + ]; + + return flags.join(" "); +}; + +export function isValidToken(token: string) { + let u = buildApiUrl(repoUrl, `/user`); + let output = safeExecSync(token, `curl ${buildCurlFlags(token)} ${u.href} 2>/dev/null`).toString("utf-8"); + let repositoryInfo = JSON.parse(output); + return repositoryInfo.message !== "Bad credentials"; +} + +export function repositoryExists(token: string, repoUrl: url.Url) { + let u = buildApiUrl(repoUrl, `/repos${repoUrl.pathname}`); + let output = safeExecSync(token, `curl ${buildCurlFlags(token)} ${u.href} 2>/dev/null`).toString("utf-8"); + let repositoryInfo = JSON.parse(output); + return repositoryInfo.message !== "Not Found"; +}; + +export function createRepository(token: string, repoUrl: url.Url) { + let u = buildApiUrl(repoUrl, `/user/repos`); + let data = { + name: _.last(repoUrl.path.split("/")), + auto_init: true, + private: false, + }; + + let options = { + hostname: u.hostname, + path: u.path, + method: "POST", + headers: { + "User-Agent": "snappit", + "Content-Type": "application/json", + "Authorization": "token " + token + } + }; + + return new Promise((resolve, reject) => { + let req = https.request(options, res => { + let data: string[]; + data = []; + res.on("data", d => { data.push(d.toString())}); + if (res.statusCode !== 201) { + res.on("end", () => { + const msg = `(HTTP ${res.statusCode}) Something went wrong while creating the repository ${repoUrl.href}:\n${data.join("")}`; + throw new Error(msg); + }); + } + res.on("end", () => { + if (!repositoryExists(token, repoUrl)) { + do { + setTimeout(() => { + console.log(`Waiting on newly created repository ${repoUrl.href} to appear...`) + }, 1000); + } while (!repositoryExists(token, repoUrl)) + } + resolve(`Created a new repository at ${repoUrl.href}`); + }); + }); + + req.write(JSON.stringify(data)); + + req.end(); + }); + +}; + +export function resetRepository(screenshotsDirectory: string) { + if (fs.existsSync(`${screenshotsDirectory}/.git`)) { + // check the current remote in case it's been updated + const remote = child_process.execSync(`cd ${screenshotsDirectory}; git remote -v | head -n 1`).toString(); + const remoteRegex = new RegExp(`\bgit@${config.githubHostname}:${config.githubName}/${config.repo}.git\b`); + if (!remoteRegex.test(remote)) { + console.log("Updated screenshots repository detected -- cleaning."); + cmd("npm run clean:screenshots"); + } + } +} + +export function cloneRepo(token: string, screenshotsDirectory: string, repoUrl: url.Url) { + let cloneUrl = `https://${token}@${repoUrl.host}${repoUrl.path}.git`; + console.log(`Cloning a screenshots project into "${path.resolve(screenshotsDirectory)}"`); + safeExecSync(token, `git clone ${cloneUrl} screenshots/ > /dev/null`) + + console.log(`Cloned a screenshots project into "${path.resolve(screenshotsDirectory)}"`); +}; + +function commitScreenshots(token: string, screenshotsDirectory: string, message: string) { + let cmds = [ + `cd ${screenshotsDirectory}`, + `git add -A`, + `git status -sb`, + `git commit -m "Checking in screenshots...${message}"`, + `cd -` + ]; + + return safeExecSync(token, cmds.join('; ')); +}; + + +if (require.main === module) { + checkConfig().catch(e => { + console.log(e.message); + process.exit(1); + }); +} diff --git a/test/bin/visreg.d.ts b/test/bin/visreg.d.ts new file mode 100644 index 000000000..e69de29bb diff --git a/test/bin/visreg.example.config.ts b/test/bin/visreg.example.config.ts new file mode 100644 index 000000000..2e5d3d2f3 --- /dev/null +++ b/test/bin/visreg.example.config.ts @@ -0,0 +1,22 @@ +/* + * This is simply exported so that typescript can give you hints + * about what your config has in it. + */ +export interface IConfig { + githubHostname: string, + githubName: string, + repo: string, +}; + +/* + * Configuration object for the local visreg service. + * Needed to associate visreg job runs to your github profile. + * Repo name can stay the same, but you should update the other + * values and then finally, copy these changes over to the (ignored) + * `visreg.config.ts` file. + */ +export var config: IConfig = { + "githubHostname": "github.rackspace.com", + "githubName": "name1234", + "repo": "snappit-helix-ui", +} diff --git a/test/bin/visreg.ts b/test/bin/visreg.ts new file mode 100644 index 000000000..7e767c024 --- /dev/null +++ b/test/bin/visreg.ts @@ -0,0 +1,66 @@ +import * as child_process from "child_process"; +import * as fs from "fs"; +import * as url from "url"; + +import * as opn from "opn" + +import * as util from "./util"; + +import {config, IConfig} from "./visreg.config"; +const screenshotsDirectory = "screenshots"; + +async function visreg( + currentBranch: string, + targetBranch?: string, +): Promise { + if (config.githubHostname === "github.rackspace.com") { + process.stdout.write("Checking connection to VPN..."); + try { + child_process.execSync("ping -t 3 -c 1 rax.io").toString().match(/1 packets transmitted, 1 packets received/); + } catch (e) { + console.log(" ✘"); + console.log("Check your VPN connection and try again."); + throw new Error(e); + } + console.log(" ✔"); + } + + const branch = await util.getBranchName(targetBranch); + const token = await util.validateToken(await util.getGithubToken()); + const repoUrl = url.parse(`https://${config.githubHostname}/${config.githubName}/${config.repo}`); + + process.exit(0); + + util.resetRepository(screenshotsDirectory); + if (!util.repositoryExists(token, repoUrl)) { + console.log(`Creating a new screenshots repository at ${repoUrl.href}`); + await util.createRepository(token, repoUrl); + } + + if (!fs.existsSync(`${screenshotsDirectory}/.git`)) { + util.cloneRepo(token, screenshotsDirectory, repoUrl); + } + + const anonymousBranch = `anon-${new Date().valueOf()}`; + console.log("Creating a new baseline..."); + const baseCommit = util.createBaseline(token, anonymousBranch, screenshotsDirectory, targetBranch); + console.log("Creating screenshot diff..."); + const afterCommit = util.createDiff(token, currentBranch, screenshotsDirectory); + util.pushBranch(token, repoUrl, anonymousBranch, screenshotsDirectory); + const remoteUrl = `${repoUrl.href}/compare/${baseCommit}...${afterCommit}`; + console.log(`Opening remote screenshot diff located at: ${remoteUrl}`); + opn(remoteUrl); +} + +if (require.main === module) { + const branch = Array.prototype.slice.call(process.argv, 2)[0]; + const currentBranch = child_process.execSync("git rev-parse --abbrev-ref HEAD").toString().trim(); + + visreg(currentBranch, branch) + .then(() => { process.exit(0); }) + .catch(err => { + child_process.execSync(`git checkout ${currentBranch} > /dev/null 2>&1`); + console.log(err.message); + process.exit(1); + }); +}; diff --git a/test/index.d.ts b/test/index.d.ts new file mode 100644 index 000000000..e69de29bb diff --git a/test/index.ts b/test/index.ts new file mode 100644 index 000000000..1ca074e88 --- /dev/null +++ b/test/index.ts @@ -0,0 +1,99 @@ +import {expect} from "chai"; +import * as _ from "lodash"; +import {By, ISize, ThenableWebDriver, WebDriver, WebElementPromise} from "selenium-webdriver"; + +import {$, snap, Snappit, IConfig} from "snappit-visual-regression"; +import * as util from "./util"; + +describe("helix", () => { + let snappit: Snappit; + let driver: any; + + describe("chrome", () => { + before(async () => { + const config: IConfig = { + browser: "chrome", + screenshotsDir: "screenshots", + logException: [ + "MISMATCH", + "NO_BASELINE", + "SIZE_DIFFERENCE", + ], + threshold: 0.1, + useDirect: true, + }; + + snappit = new Snappit(config); + driver = await snappit.start(); + await util.setViewportSize(driver, { width: 1366, height: 768 }); + driver.get("http://localhost:3000/"); + }); + + it("full-screen", async () => { + await snap("{browserName}/index"); + expect(await $("body").isDisplayed()).to.eql(true); + }); + + it("nav", async () => { + await snap("{browserName}/nav", $(util.$.nav)); + }); + + it("guides", async () => { + await util.$x(driver, "//nav/hx-reveal//header", "Guides").click(); + await snap("{browserName}/nav/guides", $(util.$.nav)); + }); + + it("components", async () => { + await util.$x(driver, "//nav/hx-reveal//header", "Components").click(); + await snap("{browserName}/nav/componenets", $(util.$.nav)); + }); + + after(async () => { + await snappit.stop(); + }); + }); + + describe("firefox", () => { + before(async () => { + const config: IConfig = { + browser: "firefox", + screenshotsDir: "screenshots", + logException: [ + "MISMATCH", + "NO_BASELINE", + "SIZE_DIFFERENCE", + ], + threshold: 0.1, + useDirect: true, + }; + + snappit = new Snappit(config); + driver = await snappit.start(); + await util.setViewportSize(driver, { width: 1366, height: 768 }); + driver.get("http://localhost:3000/"); + }); + + it("full-screen", async () => { + await snap("{browserName}/index"); + expect(await $("body").isDisplayed()).to.eql(true); + }); + + it("nav", async () => { + await snap("{browserName}/nav", $(util.$.nav)); + }); + + it("guides", async () => { + await util.$x(driver, "//nav/hx-reveal//header", "Guides").click(); + await snap("{browserName}/nav/guides", $(util.$.nav)); + }); + + it("components", async () => { + await util.$x(driver, "//nav/hx-reveal//header", "Components").click(); + await snap("{browserName}/nav/componenets", $(util.$.nav)); + }); + + after(async () => { + await snappit.stop(); + }); + }); +}); diff --git a/test/package.json b/test/package.json new file mode 100644 index 000000000..2d327bb7a --- /dev/null +++ b/test/package.json @@ -0,0 +1,34 @@ +{ + "dependencies": { + "@types/chai": "^4.0.4", + "@types/lodash": "^4.14.74", + "@types/mocha": "^2.2.43", + "@types/node": "^8.0.28", + "@types/opn": "^3.0.28", + "@types/reflect-metadata": "^0.0.5", + "@types/selenium-webdriver": "^3.0.7", + "chai": "^4.1.2", + "inquirer-promise": "^1.0.0", + "inquirer-shortcuts": "^1.1.0", + "lodash": "^4.17.4", + "mocha": "^3.5.3", + "opn": "^5.1.0", + "selenium-webdriver": "^3.5.0", + "snappit-visual-regression": "^0.6.4", + "tslint": "^5.7.0", + "typescript": "^2.5.2" + }, + "scripts": { + "build": "tsc", + "clean": "npm run clean:js && npm run clean:screenshots", + "clean:screenshots": "rm -rf screenshots", + "clean:js": "rm -rf built/", + "lint": "tslint index.ts", + "test": "npm run build && mocha --timeout 15000 built/index.js", + "previsreg": "[ -f ./bin/visreg.config.ts ] || cp bin/visreg{.example,}.config.ts", + "visreg": "npm run clean:js && npm run build && node ./built/bin/util.js && node ./built/bin/visreg.js", + "webdriver": "npm run webdriver:update && npm run webdriver:start", + "webdriver:update": "scripts/webdriver-update", + "webdriver:start": "scripts/webdriver-start" + } +} diff --git a/test/scripts/webdriver-start b/test/scripts/webdriver-start new file mode 100755 index 000000000..76aa8ef8f --- /dev/null +++ b/test/scripts/webdriver-start @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +# https://stackoverflow.com/a/4785518/881224 +LINK="https://github.com/rackerlabs/helix-ui/wiki/Testing#java" +which java >/dev/null 2>&1 || { echo ${LINK}; echo "Install java and try again. Aborting." >&2; exit 1; } + +java -Dwebdriver.gecko.driver=bin/selenium/geckodriver \ + -Dwebdriver.chrome.driver=bin/selenium/chromedriver \ + -jar bin/selenium/selenium-server-standalone.jar \ + -port 4444 diff --git a/test/scripts/webdriver-update b/test/scripts/webdriver-update new file mode 100755 index 000000000..48319ca71 --- /dev/null +++ b/test/scripts/webdriver-update @@ -0,0 +1,29 @@ +#!/usr/bin/env bash + +# https://stackoverflow.com/a/4785518/881224 +which wget >/dev/null 2>&1 || { echo "Install wget and try again. Aborting." >&2; exit 1; } + +# remove sources +find bin/selenium/. ! -name '.gitignore' -type f -exec rm -f {} + +# remove symlinks (see bottom of script, "ln -s") +rm -f node_modules/.bin/{gecko,chromedriver,selenium}* + +platform_gecko='linux64' +platform_chrome='linux64' +[ `uname` == 'Darwin' ] && platform_gecko='macos' && platform_chrome='mac64' + +TARFILE_GECKO=v0.18.0/geckodriver-v0.18.0-${platform_gecko}.tar.gz +TARFILE_CHROME=2.31/chromedriver_${platform_chrome}.zip +TARFILE_STANDALONE='3.4/selenium-server-standalone-3.4.0.jar' + +echo Downloading ${TARFILE_GECKO} +wget -nc -q https://{GH_TOKEN}@github.com/mozilla/geckodriver/releases/download/${TARFILE_GECKO} -O bin/selenium/geckodriver-v0.18.0.tar.gz +echo Downloading ${TARFILE_CHROME} +wget -nc -q https://chromedriver.storage.googleapis.com/${TARFILE_CHROME} -O bin/selenium/chromedriver_2.31.zip +echo Downloading ${TARFILE_STANDALONE} +wget -nc -q https://selenium-release.storage.googleapis.com/${TARFILE_STANDALONE} -O bin/selenium/selenium-server-standalone.jar +tar -xzf bin/selenium/geckodriver-v0.18.0.tar.gz -C bin/selenium/ +unzip -o bin/selenium/chromedriver_2.31.zip -d bin/selenium/ + +# puts `bin/selenium/*` (chromedriver/geckodriver/selenium) on the PATH via `node_modules/.bin` +cd node_modules/.bin/ && ln -s ../../bin/selenium/* . diff --git a/test/tsconfig.json b/test/tsconfig.json new file mode 100644 index 000000000..1af46d3a1 --- /dev/null +++ b/test/tsconfig.json @@ -0,0 +1,33 @@ +{ + "compilerOptions": { + "strict":true, + "strictNullChecks": false, + "declaration": true, + "target": "es6", + "lib": ["es6"], + "module": "commonjs", + "moduleResolution": "node", + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "types": [ + "reflect-metadata", + "node", + "mocha" + ], + "baseUrl": ".", + "paths": { + "*": ["types/*"] + }, + "outDir": "./built" + }, + "include": [ + "index.ts", + "bin/util.ts", + "bin/visreg.ts", + "bin/util.ts", + "bin/visreg.config.ts" + ], + "exclude": [ + "node_modules" + ] +} diff --git a/test/tslint.json b/test/tslint.json new file mode 100644 index 000000000..da7afd38a --- /dev/null +++ b/test/tslint.json @@ -0,0 +1,11 @@ +{ + "defaultSeverity": "error", + "extends": [ + "tslint:latest" + ], + "jsRules": {}, + "rules": { + "max-classes-per-file": [false, 1] + }, + "rulesDirectory": [] +} diff --git a/test/types/inquirer-shortcuts.d.ts b/test/types/inquirer-shortcuts.d.ts new file mode 100644 index 000000000..4473c2772 --- /dev/null +++ b/test/types/inquirer-shortcuts.d.ts @@ -0,0 +1,7 @@ +export interface IOptions { + default: String; + message: String; +} + +export function input(message: String, options?: IOptions): Promise; +export function password(message: String, options?: IOptions): Promise; diff --git a/test/util.ts b/test/util.ts new file mode 100644 index 000000000..9733afe43 --- /dev/null +++ b/test/util.ts @@ -0,0 +1,36 @@ +/* + * Helper functions for the tests in `index.ts`. + */ +import {By, ISize, ThenableWebDriver, WebDriver, WebElementPromise} from "selenium-webdriver"; +export async function setViewportSize ( + driver: ThenableWebDriver, + size: ISize, +) { + const jsGetPadding: string = `return { + width: window.outerWidth - window.innerWidth, + height: window.outerHeight - window.innerHeight + }`; + + const padding: ISize = await driver.executeScript(jsGetPadding) as ISize; + return driver.manage().window().setSize( + size.width + padding.width, + size.height + padding.height, + ); +} + +export function $x( + driver: ThenableWebDriver, + xpath: string, + byText = "", +) { + if (byText.length) { + xpath += `[contains(text(), '${byText}')]`; + } + + return driver.findElement(By.xpath(xpath)); +} + +// "page object" +export var $ = { + nav: ".hxApp__nav", +} diff --git a/test/yarn.lock b/test/yarn.lock new file mode 100644 index 000000000..586119c7e --- /dev/null +++ b/test/yarn.lock @@ -0,0 +1,711 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@types/chai@^4.0.4": + version "4.0.4" + resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.0.4.tgz#fe86315d9a66827feeb16f73bc954688ec950e18" + +"@types/fs-extra@^4.0.0": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-4.0.2.tgz#7b9b1bbf85962cbe029b5a83c9b530d7c75af3ba" + dependencies: + "@types/node" "*" + +"@types/lodash@^4.14.68", "@types/lodash@^4.14.74": + version "4.14.74" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.74.tgz#ac3bd8db988e7f7038e5d22bd76a7ba13f876168" + +"@types/mocha@^2.2.43": + version "2.2.43" + resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-2.2.43.tgz#03c54589c43ad048cbcbfd63999b55d0424eec27" + +"@types/node@*", "@types/node@^8.0.19", "@types/node@^8.0.28": + version "8.0.28" + resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.28.tgz#86206716f8d9251cf41692e384264cbd7058ad60" + +"@types/opn@^3.0.28": + version "3.0.28" + resolved "https://registry.yarnpkg.com/@types/opn/-/opn-3.0.28.tgz#097d0d1c9b5749573a5d96df132387bb6d02118a" + dependencies: + "@types/node" "*" + +"@types/reflect-metadata@^0.0.5": + version "0.0.5" + resolved "https://registry.yarnpkg.com/@types/reflect-metadata/-/reflect-metadata-0.0.5.tgz#9c042bfa9803d577aad4f57dfbca4b7cae4286fe" + +"@types/selenium-webdriver@^3.0.4", "@types/selenium-webdriver@^3.0.7": + version "3.0.7" + resolved "https://registry.yarnpkg.com/@types/selenium-webdriver/-/selenium-webdriver-3.0.7.tgz#5d3613d1ab3ca08b74d19683a3a7c573129ab18f" + +ansi-escapes@^1.1.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + +ansi-styles@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + +assertion-error@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.0.2.tgz#13ca515d86206da0bac66e834dd397d87581094c" + +babel-code-frame@^6.22.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" + dependencies: + chalk "^1.1.3" + esutils "^2.0.2" + js-tokens "^3.0.2" + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + +brace-expansion@^1.1.7: + version "1.1.8" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292" + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +browser-stdout@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.0.tgz#f351d32969d32fa5d7a5567154263d928ae3bd1f" + +chai@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.1.2.tgz#0f64584ba642f0f2ace2806279f4f06ca23ad73c" + dependencies: + assertion-error "^1.0.1" + check-error "^1.0.1" + deep-eql "^3.0.0" + get-func-name "^2.0.0" + pathval "^1.0.0" + type-detect "^4.0.0" + +chalk@^1.0.0, chalk@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + dependencies: + 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" + +check-error@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" + +cli-cursor@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987" + dependencies: + restore-cursor "^1.0.1" + +cli-width@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" + +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + +colors@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63" + +commander@2.9.0, commander@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" + dependencies: + graceful-readlink ">= 1.0.0" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + +concat-stream@^1.4.7: + version "1.6.0" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7" + dependencies: + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + +core-js@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.3.0.tgz#fab83fbb0b2d8dc85fa636c4b9d34c75420c6d65" + +core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + +debug@2.6.8: + version "2.6.8" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc" + dependencies: + ms "2.0.0" + +deep-eql@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df" + dependencies: + type-detect "^4.0.0" + +diff@3.2.0, diff@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-3.2.0.tgz#c9ce393a4b7cbd0b058a725c93df299027868ff9" + +dom-to-image@^2.5.2: + version "2.6.0" + resolved "https://registry.yarnpkg.com/dom-to-image/-/dom-to-image-2.6.0.tgz#8a503608088c87b1c22f9034ae032e1898955867" + +es6-promise@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.0.2.tgz#010d5858423a5f118979665f46486a95c6ee2bb6" + +escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + +esutils@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" + +exit-hook@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8" + +extend@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" + +external-editor@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-1.1.1.tgz#12d7b0db850f7ff7e7081baf4005700060c4600b" + dependencies: + extend "^3.0.0" + spawn-sync "^1.0.15" + tmp "^0.0.29" + +figures@^1.3.5: + version "1.7.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" + dependencies: + escape-string-regexp "^1.0.5" + object-assign "^4.1.0" + +fs-extra@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.2.tgz#f91704c53d1b461f893452b0c307d9997647ab6b" + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + +get-func-name@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" + +glob@7.1.1, glob@^7.0.5, glob@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8" + dependencies: + 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@^4.1.2, graceful-fs@^4.1.6: + version "4.1.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" + +"graceful-readlink@>= 1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" + +growl@1.9.2: + version "1.9.2" + resolved "https://registry.yarnpkg.com/growl/-/growl-1.9.2.tgz#0ea7743715db8d8de2c5ede1775e1b45ac85c02f" + +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + dependencies: + ansi-regex "^2.0.0" + +has-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" + +he@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" + +immediate@~3.0.5: + version "3.0.6" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + +inquirer-promise@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/inquirer-promise/-/inquirer-promise-1.0.0.tgz#45892ac3d267c9453fa0f0da1c93f945027f0559" + dependencies: + inquirer "^1.2.1" + +inquirer-shortcuts@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/inquirer-shortcuts/-/inquirer-shortcuts-1.1.0.tgz#eda7dfa3de2af11ce18f87f8e0e9f42a11c1f5ad" + dependencies: + inquirer "^1.2.1" + +inquirer@^1.2.1: + version "1.2.3" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-1.2.3.tgz#4dec6f32f37ef7bb0b2ed3f1d1a5c3f545074918" + dependencies: + ansi-escapes "^1.1.0" + chalk "^1.0.0" + cli-cursor "^1.0.1" + cli-width "^2.0.0" + external-editor "^1.1.0" + figures "^1.3.5" + lodash "^4.3.0" + mute-stream "0.0.6" + pinkie-promise "^2.0.0" + run-async "^2.2.0" + rx "^4.1.0" + string-width "^1.0.1" + strip-ansi "^3.0.0" + through "^2.3.6" + +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + dependencies: + number-is-nan "^1.0.0" + +is-promise@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" + +is-wsl@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" + +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + +js-tokens@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" + +json3@3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1" + +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + optionalDependencies: + graceful-fs "^4.1.6" + +jszip@^3.1.3: + version "3.1.4" + resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.1.4.tgz#fc323fe41bb1730348d20dd022aa4d8b57cbbcf9" + dependencies: + core-js "~2.3.0" + es6-promise "~3.0.2" + lie "~3.1.0" + pako "~1.0.2" + readable-stream "~2.0.6" + +lie@~3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/lie/-/lie-3.1.1.tgz#9a436b2cc7746ca59de7a41fa469b3efb76bd87e" + dependencies: + immediate "~3.0.5" + +lodash._baseassign@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz#8c38a099500f215ad09e59f1722fd0c52bfe0a4e" + dependencies: + lodash._basecopy "^3.0.0" + lodash.keys "^3.0.0" + +lodash._basecopy@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36" + +lodash._basecreate@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz#1bc661614daa7fc311b7d03bf16806a0213cf821" + +lodash._getnative@^3.0.0: + version "3.9.1" + resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" + +lodash._isiterateecall@^3.0.0: + version "3.0.9" + resolved "https://registry.yarnpkg.com/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz#5203ad7ba425fae842460e696db9cf3e6aac057c" + +lodash.create@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/lodash.create/-/lodash.create-3.1.1.tgz#d7f2849f0dbda7e04682bb8cd72ab022461debe7" + dependencies: + lodash._baseassign "^3.0.0" + lodash._basecreate "^3.0.0" + lodash._isiterateecall "^3.0.0" + +lodash.isarguments@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" + +lodash.isarray@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55" + +lodash.keys@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" + dependencies: + lodash._getnative "^3.0.0" + lodash.isarguments "^3.0.0" + lodash.isarray "^3.0.0" + +lodash@^4.17.4, lodash@^4.3.0: + version "4.17.4" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" + +minimatch@^3.0.2, minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + dependencies: + brace-expansion "^1.1.7" + +minimist@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + +mkdirp@0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + dependencies: + minimist "0.0.8" + +mocha@^3.5.3: + version "3.5.3" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-3.5.3.tgz#1e0480fe36d2da5858d1eb6acc38418b26eaa20d" + dependencies: + browser-stdout "1.3.0" + commander "2.9.0" + debug "2.6.8" + diff "3.2.0" + escape-string-regexp "1.0.5" + glob "7.1.1" + growl "1.9.2" + he "1.1.1" + json3 "3.3.2" + lodash.create "3.1.1" + mkdirp "0.5.1" + supports-color "3.1.2" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + +mute-stream@0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.6.tgz#48962b19e169fd1dfc240b3f1e7317627bbc47db" + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + +object-assign@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + dependencies: + wrappy "1" + +onetime@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" + +opn@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/opn/-/opn-5.1.0.tgz#72ce2306a17dbea58ff1041853352b4a8fc77519" + dependencies: + is-wsl "^1.1.0" + +os-shim@^0.1.2: + version "0.1.3" + resolved "https://registry.yarnpkg.com/os-shim/-/os-shim-0.1.3.tgz#6b62c3791cf7909ea35ed46e17658bb417cb3917" + +os-tmpdir@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + +pako@~1.0.2: + version "1.0.6" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.6.tgz#0101211baa70c4bca4a0f63f2206e97b7dfaf258" + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + +path-parse@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1" + +pathval@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.0.tgz#b942e6d4bde653005ef6b71361def8727d0645e0" + +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + +pngjs@3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-3.2.0.tgz#fc9fcea1a8a375da54a51148019d5abd41dbabde" + +process-nextick-args@~1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" + +readable-stream@^2.2.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~1.0.6" + safe-buffer "~5.1.1" + string_decoder "~1.0.3" + util-deprecate "~1.0.1" + +readable-stream@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e" + dependencies: + 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" + +resolve@^1.3.2: + version "1.4.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.4.0.tgz#a75be01c53da25d934a98ebd0e4c4a7312f92a86" + dependencies: + path-parse "^1.0.5" + +restore-cursor@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541" + dependencies: + exit-hook "^1.0.0" + onetime "^1.0.0" + +rimraf@^2.5.4: + version "2.6.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" + dependencies: + glob "^7.0.5" + +run-async@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" + dependencies: + is-promise "^2.1.0" + +rx@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/rx/-/rx-4.1.0.tgz#a5f13ff79ef3b740fe30aa803fb09f98805d4782" + +safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" + +sax@>=0.6.0: + version "1.2.4" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + +selenium-webdriver@3.5.0, selenium-webdriver@^3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/selenium-webdriver/-/selenium-webdriver-3.5.0.tgz#9036c82874e6c0f5cbff0a0f18223bc31c99cb77" + dependencies: + jszip "^3.1.3" + rimraf "^2.5.4" + tmp "0.0.30" + xml2js "^0.4.17" + +semver@^5.3.0: + version "5.4.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e" + +snappit-visual-regression@^0.6.4: + version "0.6.4" + resolved "https://registry.yarnpkg.com/snappit-visual-regression/-/snappit-visual-regression-0.6.4.tgz#67a7ba05beac1a5142b7498b16f3fc32d2409dca" + dependencies: + "@types/fs-extra" "^4.0.0" + "@types/lodash" "^4.14.68" + "@types/node" "^8.0.19" + "@types/selenium-webdriver" "^3.0.4" + dom-to-image "^2.5.2" + fs-extra "^4.0.1" + lodash "^4.17.4" + pngjs "3.2.0" + selenium-webdriver "3.5.0" + +spawn-sync@^1.0.15: + version "1.0.15" + resolved "https://registry.yarnpkg.com/spawn-sync/-/spawn-sync-1.0.15.tgz#b00799557eb7fb0c8376c29d44e8a1ea67e57476" + dependencies: + concat-stream "^1.4.7" + os-shim "^0.1.2" + +string-width@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + +string_decoder@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab" + dependencies: + safe-buffer "~5.1.0" + +strip-ansi@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + dependencies: + ansi-regex "^2.0.0" + +supports-color@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.1.2.tgz#72a262894d9d408b956ca05ff37b2ed8a6e2a2d5" + dependencies: + has-flag "^1.0.0" + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + +through@^2.3.6: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + +tmp@0.0.30: + version "0.0.30" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.30.tgz#72419d4a8be7d6ce75148fd8b324e593a711c2ed" + dependencies: + os-tmpdir "~1.0.1" + +tmp@^0.0.29: + version "0.0.29" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.29.tgz#f25125ff0dd9da3ccb0c2dd371ee1288bb9128c0" + dependencies: + os-tmpdir "~1.0.1" + +tslib@^1.7.1: + version "1.7.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.7.1.tgz#bc8004164691923a79fe8378bbeb3da2017538ec" + +tslint@^5.7.0: + version "5.7.0" + resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.7.0.tgz#c25e0d0c92fa1201c2bc30e844e08e682b4f3552" + dependencies: + babel-code-frame "^6.22.0" + colors "^1.1.2" + commander "^2.9.0" + diff "^3.2.0" + glob "^7.1.1" + minimatch "^3.0.4" + resolve "^1.3.2" + semver "^5.3.0" + tslib "^1.7.1" + tsutils "^2.8.1" + +tsutils@^2.8.1: + version "2.8.2" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.8.2.tgz#2c1486ba431260845b0ac6f902afd9d708a8ea6a" + dependencies: + tslib "^1.7.1" + +type-detect@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.3.tgz#0e3f2670b44099b0b46c284d136a7ef49c74c2ea" + +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + +typescript@^2.5.2: + version "2.5.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.5.2.tgz#038a95f7d9bbb420b1bf35ba31d4c5c1dd3ffe34" + +universalify@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.1.tgz#fa71badd4437af4c148841e3b3b165f9e9e590b7" + +util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + +xml2js@^0.4.17: + version "0.4.19" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.19.tgz#686c20f213209e94abf0d1bcf1efaa291c7827a7" + dependencies: + sax ">=0.6.0" + xmlbuilder "~9.0.1" + +xmlbuilder@~9.0.1: + version "9.0.4" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.4.tgz#519cb4ca686d005a8420d3496f3f0caeecca580f"