├── .yarnrc ├── test ├── fixtures │ ├── first.png │ └── second.png ├── mocha.opts ├── common.js ├── sanity.spec.js ├── utils │ ├── data-object-path.spec.js │ ├── data-object-wait.spec.js │ ├── data-object-vw.spec.js │ ├── data-object-vh.spec.js │ ├── data-object-threshold.spec.js │ ├── data-object.spec.js │ ├── reference-item.spec.js │ ├── config-file.spec.js │ └── results-to-json.spec.js ├── adapter │ ├── blink-diff.spec.js │ └── puppeteer.spec.js ├── api │ ├── fresh-reference.spec.js │ └── check-regression.spec.js └── main.spec.js ├── .travis.yml ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .editorconfig ├── .jsdoc.json ├── LICENSE.md ├── package.json ├── app ├── utils │ ├── config-file.js │ ├── reference-item.js │ ├── results-to-json.js │ └── data-object.js ├── api │ ├── fresh-reference.js │ └── check-regression.js ├── main.js └── adapter │ ├── blink-diff.js │ └── puppeteer.js ├── README.md └── yarn.lock /.yarnrc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/first.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinkr/chigai-core/HEAD/test/fixtures/first.png -------------------------------------------------------------------------------- /test/fixtures/second.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinkr/chigai-core/HEAD/test/fixtures/second.png -------------------------------------------------------------------------------- /test/mocha.opts: -------------------------------------------------------------------------------- 1 | ./test/**/*.spec.js 2 | --require ./test/common.js 3 | --recursive 4 | --timeout 20000 5 | 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "8" 4 | - "node" 5 | 6 | sudo: required 7 | addons: 8 | chrome: stable 9 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | ./build/* 2 | ./node_modules/* 3 | ./dev/clientlib-all/js/build/*.js 4 | *lazy*.js 5 | *.min.js 6 | *.bundle.js 7 | ./dev/test/spec/* 8 | _*.js 9 | ./feops/* 10 | -------------------------------------------------------------------------------- /test/common.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | global.chai = require("chai"); 4 | global.chai.should(); 5 | 6 | global.expect = global.chai.expect; 7 | 8 | global.sinon = require("sinon"); 9 | global.sinonChai = require("sinon-chai"); 10 | global.chai.use(global.sinonChai); 11 | 12 | // global.chaiAsPromised = require("chai-as-promised"); 13 | // global.chai.use(global.chaiAsPromised); 14 | 15 | global.ENV = "MOCHA"; 16 | 17 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | "root": true, 4 | "extends": "eslint:recommended", 5 | "parserOptions": { "ecmaVersion": 8 }, 6 | "env": { 7 | "node": true, 8 | "es6": true 9 | 10 | }, 11 | "globals": { 12 | "ENV": "", 13 | "ENV_LOGLEVEL": "" // "verboose" 14 | }, 15 | 16 | "rules": { 17 | "no-console": "off" 18 | } 19 | } 20 | 21 | // sane 22 | // preserve native functionality 23 | // explicit, readable code 24 | // be as verboose as possible 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | #system files 3 | Thumbs.db 4 | ehthumbs.db 5 | 6 | # dotfiles 7 | .brackets* 8 | .vscode 9 | .DS_Store 10 | .tern-port 11 | .nyc_output 12 | 13 | # node related things 14 | .cache/ 15 | .node-gyp/ 16 | 17 | 18 | # ignore dependecies 19 | bower_components 20 | node_modules 21 | 22 | # istanbul coverage reports 23 | coverage 24 | 25 | # documentation 26 | docs 27 | 28 | # ignore misc files 29 | **/reports/** 30 | **/logs/** 31 | *.log 32 | notes.md 33 | 34 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # This file is for unifying the coding style for different editors and IDEs 2 | # editorconfig.org 3 | 4 | root=true 5 | 6 | [*] 7 | end_of_line=lf 8 | charset=utf-8 9 | trim_trailing_whitespace=true 10 | insert_final_newline=true 11 | 12 | 13 | # Tabs in CSS, LESS, SASS,HTML 14 | [*.js] 15 | indent_style=tab 16 | [*.html] 17 | indent_style=tab 18 | [*.css] 19 | indent_style=tab 20 | [*.scss] 21 | indent_style=tab 22 | [*.sass] 23 | indent_style=tab 24 | [*.less] 25 | indent_style=tab 26 | 27 | # Spaces in JSON 28 | [*.json] 29 | indent_style=space 30 | # 31 | [*.*rc] 32 | indent_style=space 33 | # 34 | 35 | [*.md] 36 | insert_final_newline=false 37 | -------------------------------------------------------------------------------- /.jsdoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "tags": { 3 | "allowUnknownTags": true, 4 | "dictionaries": ["jsdoc"] 5 | }, 6 | "source": { 7 | "include": ["./app"], 8 | "includePattern": ".js$", 9 | "excludePattern": "(node_modules/|docs)" 10 | }, 11 | "sourceType" : "module", 12 | "plugins": [ 13 | "plugins/markdown", 14 | "plugins/summarize" 15 | ], 16 | "templates": { 17 | "cleverLinks": true, 18 | "monospaceLinks": true, 19 | "useLongnameInNav": true, 20 | "showInheritedInNav": true 21 | }, 22 | "opts": { 23 | "template": "./node_modules/loke-jsdoc-theme", 24 | "destination": "./docs/jsdoc", 25 | "encoding": "utf8", 26 | "private": true, 27 | "recurse": true 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2016, 2017 Martin Krause 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /test/sanity.spec.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 3 | const fs = require("fs-extra-plus"); 4 | 5 | const timeout = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250)); 6 | 7 | 8 | describe("sanity checks for the mocha test suite", () => { 9 | describe("the mocha async wrapper should work as expected", () => { 10 | 11 | it("Simple async/await mocha test", (async () => { 12 | let x = await timeout(() => "Hello World!"); 13 | (x).should.equal("Hello World!"); 14 | })); 15 | 16 | it("the mocha async wrapper should work as expected with mutliple async / awaits", (async () => { 17 | let x = await timeout(() => "Hello World!x"); 18 | let y = await timeout(() => "Hello World!y"); 19 | let z = await timeout(() => "Hello World!z"); 20 | (x).should.equal("Hello World!x"); 21 | (y).should.equal("Hello World!y"); 22 | (z).should.equal("Hello World!z"); 23 | })); 24 | 25 | it("the mocha async wrapper should work with the filesystem", (async () => { 26 | let _fileTrue = "./test/sanity.spec.js"; 27 | let _fileFalse = "./test/none.txt"; 28 | let x = await timeout(() => fs.pathExists(_fileTrue)); 29 | let y = await timeout(() => fs.pathExists(_fileFalse)); 30 | (x).should.be.ok; 31 | (y).should.not.be.ok; 32 | })); 33 | 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chigai-core", 3 | "version": "1.5.3", 4 | "description": "Chigai: modern visual regression testing. CORE module.", 5 | "main": "./app/main.js", 6 | "license": "MIT", 7 | "engines": { 8 | "node": ">=8.5.5" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/martinkr/chigai-core" 13 | }, 14 | "homepage": "https://github.com/martinkr/chigai-core", 15 | "issues": "https://github.com/martinkr/chigai-core/issues", 16 | "author": "Martin Krause (http://martinkr.github.io)", 17 | "keywords": [ 18 | "visual", 19 | "regression", 20 | "testing", 21 | "ci", 22 | "continious integration", 23 | "bdd", 24 | "tdd", 25 | "mocha", 26 | "chai", 27 | "node", 28 | "puppeteer", 29 | "css regression" 30 | ], 31 | "scripts": { 32 | "eslint": "./node_modules/.bin/eslint ./app/**/*.js", 33 | "mocha": "./node_modules/mocha/bin/mocha", 34 | "nyc": "./node_modules/.bin/nyc --clean ./node_modules/.bin/mocha --exit", 35 | "coverage": "./node_modules/.bin/nyc check-coverage --lines 100 --functions 100 --branches 100 --statements 100", 36 | "report": "./node_modules/.bin/nyc report --reporter=lcov --reporter=html", 37 | "test": "yarn eslint && yarn nyc && yarn report && yarn coverage" 38 | }, 39 | "dependencies": { 40 | "blink-diff": "^1.0.13", 41 | "fs-extra-plus": "^0.1.3", 42 | "puppeteer": "^1.13.0" 43 | }, 44 | "devDependencies": { 45 | "chai": "4.1.1", 46 | "chigai-mock-server": "^1.0.0", 47 | "eslint": "4.18.2", 48 | "image-size": "https://github.com/image-size/image-size", 49 | "mocha": "5.0.3", 50 | "nyc": "^13.2.0", 51 | "proxyquire": "^1.8.0", 52 | "sinon": "^4.4.2", 53 | "sinon-chai": "^3.0.0" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /app/utils/config-file.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @module utils/confif-file 3 | * @exports an async function 4 | * @description 5 | * This module checks if there's a config file 6 | * It provides an API for getting config options 7 | * and makes sure all modules can work with the options 8 | * 9 | * @copyright 2017 Martin Krause (http://martinkr.github.io) 10 | * @license MIT license: https://opensource.org/licenses/MIT 11 | * 12 | * @author Martin Krause 13 | */ 14 | 15 | // imports 16 | const fs = require("fs-extra-plus"); 17 | const path = require("path"); 18 | const defaults = { "from": "default", "path": path.join(process.cwd(), "./screenshots") , "vw": 1024,"vh": 786, "threshold": 0.01, "wait": 0}; 19 | const rcfile = path.join(process.cwd(), ".chigairc.json"); 20 | 21 | /** 22 | * Loads an verifies the configuration file settings 23 | * @async 24 | * @exports an async function 25 | * @memberof module:utils/config-file 26 | * @returns {Object} an object all settings from the rc-file 27 | */ 28 | module.exports = async() => { 29 | try { 30 | let chigairc; 31 | let screenshotPath; 32 | chigairc = await fs.readJson(rcfile); 33 | 34 | // check the path 35 | if(chigairc.path) { 36 | screenshotPath = path.join(process.cwd(), chigairc.path) 37 | } else { 38 | screenshotPath = defaults.path; 39 | } 40 | await fs.ensureDir(screenshotPath); 41 | 42 | if (!chigairc.threshold) { 43 | chigairc.threshold = defaults.threshold; 44 | } 45 | 46 | if (!chigairc.wait) { 47 | chigairc.wait = defaults.wait; 48 | } 49 | 50 | if (!chigairc.vh) { 51 | chigairc.vh = defaults.vh; 52 | } 53 | 54 | if(!chigairc.vw) { 55 | chigairc.vw = defaults.vw; 56 | } 57 | 58 | // return options 59 | return { 60 | "from": rcfile, 61 | "path": screenshotPath, 62 | "threshold": chigairc.threshold, 63 | "vh": chigairc.vh, 64 | "vw": chigairc.vw, 65 | "wait": chigairc.wait 66 | }; 67 | } catch (error) { 68 | // return defaults 69 | return defaults; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /app/utils/reference-item.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @module utils/reference-item 3 | * @exports an async function 4 | * @description 5 | * This module ensures the existence of a reference item. 6 | * It creates a new reference item for the comparison if none exists. 7 | * The fresh reference item is just a copy of the regression item. 8 | * It sets the flag "fresh" on the data-object: 9 | * - true if it had to create a fresh reference item 10 | * - false if the reference item already existed 11 | * 12 | * @copyright 2017 Martin Krause (http://martinkr.github.io) 13 | * @license MIT license: https://opensource.org/licenses/MIT 14 | * 15 | * @author Martin Krause 16 | */ 17 | 18 | // imports 19 | const fs = require("fs-extra-plus"); 20 | const currentModule = "utils/reference-item"; 21 | 22 | /** 23 | * Ensures the existence of a reference item 24 | * @async 25 | * @exports an async function 26 | * @memberof module:utils/reference-item 27 | * @param {object} with the properties "uri" the location to screenshot and "[options]" additional options, e.g. viewport sizes 28 | * @returns {Object} an object all necessary properties for this item 29 | */ 30 | module.exports = async(item) => { 31 | 32 | if (!item || Array.isArray(item) || typeof(item) !== "object") { 33 | throw new Error(`${currentModule} missing arguments`); 34 | } 35 | 36 | let regressionItemAvailable = await fs.pathExists(item.regression_item); 37 | if (regressionItemAvailable !== true) { 38 | throw new Error(`${currentModule} missing regression item ${item.regression_item}`); 39 | } 40 | 41 | let referenceItemAvailable = await fs.pathExists(item.reference_item); 42 | if (referenceItemAvailable !== true) { 43 | try { 44 | await fs.copy(item.regression_item, item.reference_item); 45 | item.fresh = true; 46 | } catch (error) { 47 | /* istanbul ignore next */ 48 | throw new Error(`${currentModule} failed on copy ${item.regression_item} to ${item.reference_item}`); 49 | } 50 | } else { 51 | item.fresh = false; 52 | } 53 | 54 | return item; 55 | } 56 | -------------------------------------------------------------------------------- /app/api/fresh-reference.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @module api/fresh-reference 3 | * @exports an async function 4 | * @description 5 | * Creates a fresh reference for a given set of items. 6 | * It ensures the arguments have the correct form. 7 | * It creates the data-objects. 8 | * It removes the current reference item. 9 | * It creates a new screenshot (regression item). 10 | * It sets the freshly created screenshot as regression item. 11 | * It sets the "fresh" flag to true. 12 | * 13 | * @copyright 2017 Martin Krause (http://martinkr.github.io) 14 | * @license MIT license: https://opensource.org/licenses/MIT 15 | * 16 | * @author Martin Krause 17 | */ 18 | 19 | // imports 20 | const fs = require("fs-extra-plus"); 21 | const screenshot = require("./../adapter/puppeteer"); 22 | const reference = require("./../utils/reference-item"); 23 | const dataObject = require("./../utils/data-object"); 24 | 25 | // whoami 26 | const currentModule = "api/fresh-reference"; 27 | 28 | /** 29 | * Handles the regression test for a list of items. 30 | * Each item requires a property "uri" and can have 31 | * an optional set of options. 32 | * @async 33 | * @exports an async function 34 | * @memberof module:api/fresh-reference 35 | * @param {Array} items an array of items with uri and options 36 | * @returns {Array} an array of object with the results 37 | */ 38 | module.exports = async (items) => { 39 | if (!items || Array.isArray(items) === false) { 40 | throw new Error(`${currentModule} missing items`); 41 | } 42 | 43 | items = await Promise.all(items.map(async (item) => { 44 | return await dataObject(item); 45 | })); 46 | 47 | items = await Promise.all(items.map(async (item) => { 48 | let screenshotItem = item; 49 | await fs.ensureDir(screenshotItem.path); 50 | await fs.remove(screenshotItem.reference_item); 51 | await fs.ensureDir(screenshotItem.path); 52 | screenshotItem = await screenshot(screenshotItem); 53 | screenshotItem = await reference(screenshotItem); 54 | return screenshotItem; 55 | })); 56 | 57 | return items; 58 | }; 59 | -------------------------------------------------------------------------------- /app/main.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @module app/main 4 | * @exports an async function 5 | * 6 | * @description 7 | * "Chigai: css regression made simple" 8 | * The chigai main entry point. 9 | * Defines the API fo the core components 10 | * 11 | * regression: makes regression test 12 | * reference: sets a fresh reference 13 | * 14 | * @copyright 2017 Martin Krause (http://martinkr.github.io) 15 | * @license MIT license: https://opensource.org/licenses/MIT 16 | * 17 | * @author Martin Krause 18 | */ 19 | 20 | const regression = require("./api/check-regression.js"); 21 | const reference = require("./api/fresh-reference.js"); 22 | 23 | const API = { 24 | 25 | /** 26 | * Handles the regression test for an item. 27 | * Each item requires a property "uri" and can have 28 | * an optional set of options. 29 | * @async 30 | * @exports an async function 31 | * @memberof module:app/main 32 | * @param {String} uri the uri to screenshot 33 | * @param {Object} [options] additional options, vw, vh, threshold 34 | * @returns {Array} an array of object with the results 35 | */ 36 | "regression": async (uri, options) => { 37 | if (options.d) { 38 | console.log(`[chigai-core] called \"regression\" on: \"${uri}\" with \"${options}\"`); 39 | return false; 40 | } 41 | return await regression([{ "uri": uri, "options": options }]); 42 | }, 43 | 44 | /** 45 | * Creates a new reference for an item. 46 | * Each item requires a property "uri" and can have 47 | * an optional set of options. 48 | * @async 49 | * @exports an async function 50 | * @memberof module:app/main 51 | * @param {String} uri the uri to screenshot 52 | * @param {Object} [options] additional options, vw, vh, threshold 53 | * @returns {Array} an array of object with the results 54 | */ 55 | "reference": async (uri, options) => { 56 | if (options.d) { 57 | console.log(`[chigai-core] called \"reference\" on: \"${uri}\" with \"${options}\"`); 58 | return false; 59 | } 60 | return await reference([{ "uri": uri, "options": options }]); 61 | } 62 | }; 63 | 64 | module.exports = API; 65 | -------------------------------------------------------------------------------- /test/utils/data-object-path.spec.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 3 | const fs = require("fs-extra-plus"); 4 | const path = require("path"); 5 | const crypto = require("crypto"); 6 | 7 | const thisModulePath = "data-object"; 8 | const thisModule = require("./../../app/utils/" + thisModulePath); 9 | 10 | 11 | const rcfile = path.join(process.cwd(), ".chigairc.json"); 12 | const customPath = "./test/_tmp/custom"; 13 | 14 | /** creates a data-object as argument */ 15 | const createItem = (uri, options) => { 16 | let item = { "viewport": {} }; 17 | item.uri = uri; 18 | item.options = options; 19 | return item; 20 | }; 21 | 22 | describe(`the module ${thisModulePath}`, () => { 23 | 24 | describe("should work as expected for the option \"path\". It", () => { 25 | 26 | it("should return a data object with: a path for the screenshots", (async () => { 27 | let result; 28 | result = await thisModule(createItem("http://", {})); 29 | result.path.should.be.a("string"); 30 | })); 31 | 32 | it("should return a data object with: a default path \"./screenshots\"", (async () => { 33 | let result; 34 | result = await thisModule(createItem("http://", {})); 35 | result.path.should.equal(path.join(process.cwd(), "./screenshots")); 36 | })); 37 | 38 | it("should return a data object with: a custom path if there's a .chigairc.json file", (async () => { 39 | await fs.remove(rcfile); 40 | await fs.writeFile(rcfile, JSON.stringify({"path": customPath})); 41 | let result; 42 | result = await thisModule(createItem("http://")); 43 | await fs.remove(rcfile); 44 | result.path.should.equal(path.resolve(customPath)); 45 | })); 46 | 47 | it("should return a data object where the passed options take precedence over .chigairc.json for \"path\"", (async () => { 48 | await fs.remove(rcfile); 49 | await fs.writeFile(rcfile, JSON.stringify({"path": customPath})); 50 | let result; 51 | let expectation = "foobar" 52 | let item = createItem("http://",{ "path": expectation}); 53 | result = await thisModule(item); 54 | await fs.remove(rcfile); 55 | result.path.should.equal(path.resolve(expectation)); 56 | })); 57 | 58 | }); 59 | }); 60 | -------------------------------------------------------------------------------- /app/api/check-regression.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @module api/check-regression 3 | * @exports an async function 4 | * @description 5 | * Handles the regresion test for the provided items. 6 | * It ensures the arguments have the correct form. 7 | * It creates the data-objects. 8 | * It makes screenshots. 9 | * It ensrures that there are reference items. 10 | * It compares the screenshot (regression item) against the reference item. 11 | * It returns an array of results. 12 | * 13 | * 14 | * @copyright 2017 Martin Krause (http://martinkr.github.io) 15 | * @license MIT license: https://opensource.org/licenses/MIT 16 | * 17 | * @author Martin Krause 18 | */ 19 | 20 | // imports 21 | const fs = require("fs-extra-plus"); 22 | const compare = require("./../adapter/blink-diff"); 23 | const screenshot = require("./../adapter/puppeteer"); 24 | const reference = require("./../utils/reference-item"); 25 | const toJson = require("./../utils/results-to-json"); 26 | const dataObject = require("./../utils/data-object"); 27 | 28 | // whoami 29 | const currentModule = "api/check-regression" 30 | 31 | /** 32 | * Handles the regression test for a list of items. 33 | * Each item requires a property "uri" and can have 34 | * an optional set of options. 35 | * @async 36 | * @exports an async function 37 | * @memberof module:api/check-regression 38 | * @param {Array} items an array of items with uri and options 39 | * @returns {Array} an array of object with the results 40 | */ 41 | module.exports = async (items) => { 42 | if (!items || Array.isArray(items) === false) { 43 | throw new Error(`${currentModule} missing items`); 44 | } 45 | 46 | items = await Promise.all(items.map(async (item) => { 47 | return await dataObject(item); 48 | })); 49 | 50 | items = await Promise.all(items.map(async (item) => { 51 | let screenshotItem = item; 52 | await fs.ensureDir(screenshotItem.path); 53 | screenshotItem = await screenshot(screenshotItem); 54 | screenshotItem = await reference(screenshotItem); 55 | screenshotItem = await compare(screenshotItem); 56 | return screenshotItem; 57 | })); 58 | 59 | try { 60 | await toJson.write(items); 61 | } catch (err) { 62 | /* istanbul ignore next */ 63 | return err; 64 | } 65 | 66 | return items; 67 | }; 68 | -------------------------------------------------------------------------------- /app/adapter/blink-diff.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @module adapter/blink-diff 3 | * @exports a promise for async/await. 4 | * @description 5 | * The blink-diff adapter. 6 | * Integrates blink-diff for image comparison. 7 | * It compares the regression item (the newly created screenshot) 8 | * and the reference item (the one set as reference). 9 | * It save the comparison result as difference item. 10 | * It sets the flag "match" on the data-object: 11 | * - true if the comparison is successfull (below threshold) 12 | * - false if the comparison is not successfull (above threshold) 13 | * 14 | * "A lightweight image comparison tool http://yahoo.github.io/blink-diff/" 15 | * @see https://github.com/yahoo/blink-diff 16 | * 17 | * @copyright 2016, 2017 Martin Krause (http://martinkr.github.io) 18 | * @license MIT license: https://opensource.org/licenses/MIT 19 | * 20 | * @author Martin Krause 21 | */ 22 | 23 | // imports 24 | const BlinkDiff = require("blink-diff"); 25 | // whoami 26 | const currentModule = "adapter/blink-diff"; 27 | 28 | /** 29 | * Compares the reference_item and the regression_item (the new one). 30 | * Uses https://github.com/yahoo/blink-diff for the actual image comparison. 31 | * @async 32 | * @exports a promise for async/await. 33 | * @memberof module:adapter/blink-diff 34 | * @param {Object} item a data-object generated by ".data-object.js" 35 | * @returns {Object} a promise of the data-object with a new property "match", "true" if the images are the same - "false" otherwise 36 | */ 37 | module.exports = function (item) { 38 | return new Promise( (resolve) => { 39 | /** Create a new BlinkDiff insance */ 40 | const blinkDiff = new BlinkDiff({ 41 | imageAPath: item.reference_item, 42 | imageBPath: item.regression_item, 43 | // composition: false, 44 | thresholdType: BlinkDiff.THRESHOLD_PERCENT, 45 | threshold: item.threshold, //0.01 = 1% threshold 46 | imageOutputPath: item.difference_item 47 | }); 48 | 49 | /** run the actual comparison on the blink-diff instance */ 50 | blinkDiff.run( (error, result) => { 51 | // i was not able to force BlinkDiff run to emit an error :D 52 | /* istanbul ignore if */ 53 | if (error) { 54 | throw new Error(`${currentModule}: ${error}`); 55 | } else { 56 | // console.log(`${currentModule} result.code: ${result.code} / ${blinkDiff.hasPassed(result.code) ? 'Passed' : 'Failed'}. Found ${result.differences} differences.`); 57 | if ( blinkDiff.hasPassed(result.code) ) { 58 | //passed 59 | item.match = true; 60 | resolve(item); 61 | } else { 62 | // fail 63 | item.match = false; 64 | resolve(item); 65 | } 66 | } 67 | }); 68 | 69 | }); 70 | }; 71 | -------------------------------------------------------------------------------- /app/utils/results-to-json.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @module utils/results-to-json 3 | * @exports an async function 4 | * @description 5 | * This module writes the result items to the results.json 6 | * in the screenshot directory of the chigai instance 7 | * 8 | * @copyright 2018 Martin Krause (http://martinkr.github.io) 9 | * @license MIT license: https://opensource.org/licenses/MIT 10 | * 11 | * @author Martin Krause 12 | */ 13 | 14 | // imports 15 | const fs = require("fs-extra-plus"); 16 | const path = require("path"); 17 | const dataFile = "results.json"; 18 | 19 | /** 20 | * Reads the file content 21 | * @param {String} location the file's paths 22 | * @return {Array} the file's content or an empty array 23 | */ 24 | const _getContentFromFile = async (location) => { 25 | let _fileContent; 26 | 27 | try { 28 | _fileContent = await fs.readJson(location); 29 | } catch (err) { 30 | _fileContent = []; 31 | } 32 | 33 | if (Array.isArray(_fileContent) === false) { 34 | _fileContent = []; 35 | } 36 | 37 | return _fileContent; 38 | }; 39 | 40 | /** 41 | * Updates the content array received from the file. 42 | * Adds new items and updates existing ones. 43 | * @param {Array} the file's content array 44 | * @param {Object} the new item 45 | * @return {Array} the updated content array 46 | */ 47 | const _updateContent = (array, newItem) => { 48 | 49 | const _index = array.findIndex((item) => { 50 | return item.hash === newItem.hash && item.path === newItem.path; 51 | }); 52 | 53 | if (_index === -1) { 54 | array.push(newItem); 55 | } 56 | else { 57 | array.splice(_index, 1, newItem); 58 | } 59 | 60 | return array; 61 | }; 62 | 63 | /** 64 | * Writes the result items to a json file 65 | * @async 66 | * @exports an async function 67 | * @memberof module:utils/results-to-json 68 | * @returns {Object} the original result items 69 | */ 70 | 71 | const _write = async (data) => { 72 | 73 | if (Array.isArray(data) === false) { 74 | throw new Error(); 75 | } 76 | 77 | try { 78 | await Promise.all(data.map(async (dataItem) => { 79 | let _fileContent; 80 | let _filePath = path.join(dataItem.path, "/", dataFile); 81 | try { 82 | await fs.ensureFile(_filePath); 83 | _fileContent = await _getContentFromFile(_filePath); 84 | _fileContent = _updateContent(_fileContent, dataItem); 85 | await fs.writeJson(_filePath, _fileContent); 86 | } catch (err) { 87 | /* istanbul ignore next */ 88 | throw new Error(err); 89 | } 90 | return true; 91 | })); 92 | } catch (err) { 93 | /* istanbul ignore next */ 94 | throw new Error(err); 95 | } 96 | 97 | return true; 98 | 99 | }; 100 | 101 | 102 | module.exports.write = _write; 103 | -------------------------------------------------------------------------------- /app/utils/data-object.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @module data-object 3 | * @exports a sync function 4 | * @description 5 | * This moduel creates the argument "data object". 6 | * This module validates all properties on the "data object" 7 | * The object has all required properties. 8 | * Necesary for all downstream functions. 9 | * 10 | * @copyright 2017 Martin Krause (http://martinkr.github.io) 11 | * @license MIT license: https://opensource.org/licenses/MIT 12 | * 13 | * @author Martin Krause 14 | */ 15 | 16 | // imports 17 | const path = require("path"); 18 | const crypto = require("crypto"); 19 | const configFile = require("./config-file.js"); 20 | 21 | // whoami 22 | const currentModule = "data-object" 23 | 24 | /** 25 | * Creates and valdiates the data object 26 | * @TODO: WRITE TESTS FOR PASSED OPTIONS TAKES PRECEDENCE 27 | * @sync 28 | * @exports an async function 29 | * @memberof module:data-object 30 | * @param {object} with the properties "uri" the location to screenshot and "[options]" additional options, e.g. viewport sizes 31 | * @returns {Object} an object containing all necessary properties 32 | */ 33 | module.exports = async (object) => { 34 | 35 | const uri = object.uri; 36 | const config = await configFile(); 37 | const options = Object.assign({}, config, object.options); 38 | const screenshotsPath = options.path; 39 | 40 | if (!uri || typeof (uri) !== "string") { 41 | throw new Error(`${currentModule} missing location item.uri`); 42 | } 43 | 44 | let item = { viewport: {} }; 45 | 46 | item.uri = uri; 47 | 48 | // taken from .rcfile < object.options 49 | item.threshold = !!Number(options.threshold) && typeof(options.threshold) !== "boolean" ? Number(options.threshold) : config.threshold; 50 | item.viewport.width = !!Number(options.vw) && typeof(options.vw) !== "boolean" ? Number(options.vw) : config.vw; 51 | item.viewport.height = !!Number(options.vh) && typeof(options.vh) !== "boolean" ? Number(options.vh) : config.vh; 52 | item.wait = !!Number(options.wait) && typeof(options.wait) !== "boolean" ? Number(options.wait) : config.wait; 53 | item.path = path.resolve(screenshotsPath); 54 | 55 | // internal 56 | item.timestamp_iso = new Date(new Date().getTime()).toString(); 57 | item.hash = crypto.createHash("sha512").update(uri + item.viewport.width + item.viewport.height + item.wait).digest("hex"); 58 | item.regression_item = path.resolve(screenshotsPath, item.hash+"_regression.png"); 59 | item.reference_item = path.resolve(screenshotsPath, item.hash+"_reference.png"); 60 | item.difference_item = path.resolve(screenshotsPath, item.hash + "_difference.png"); 61 | item.match = undefined; 62 | item.screenshot = undefined; 63 | item.fresh = undefined; 64 | // item.debug = ` ${item.uri}: vw ${item.viewport.width}, vh${item.viewport.height} => hash: ${item.hash}`; 65 | 66 | return item; 67 | } 68 | -------------------------------------------------------------------------------- /app/adapter/puppeteer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @module adapter/puppeteer 3 | * @exports an async function 4 | * @description 5 | * The puppeteet adapter. 6 | * This module integrates chrome's headless node API 7 | * It makes screenshots from the given uris and saves them as PNG files. 8 | * It creates the PNG used as regression item. 9 | * It sets the flag "screenshot" on the data-object: 10 | * - true if the regression item was created successfully 11 | * - false if the regression item was not created successfully 12 | * 13 | * "Puppeteer is a Node library which provides a high-level API to control headless Chrome over the DevTools Protocol." 14 | * @see https://github.com/GoogleChrome/puppeteer 15 | * 16 | * @copyright 2017 Martin Krause (http://martinkr.github.io) 17 | * @license MIT license: https://opensource.org/licenses/MIT 18 | * 19 | * @author Martin Krause 20 | */ 21 | 22 | // imports 23 | const puppeteer = require("puppeteer"); 24 | const fs = require("fs-extra-plus"); 25 | 26 | // whoami 27 | const currentModule = "adapter/puppeteer"; 28 | 29 | /** 30 | * Creates the screenshots (regression_item) for the comparison. 31 | * Uses https://github.com/GoogleChrome/puppeteer as headless node API 32 | * @async 33 | * @exports an async function 34 | * @memberof module:adapter/puppeteer 35 | * @param {Object} item a data-object generated by ".data-object.js" 36 | * @returns {Object|Error} a promise of the data-object with a new property "screenshot", "true" if the image was generated or throws an error 37 | */ 38 | module.exports = async (item) => { 39 | 40 | 41 | let browser; 42 | let page; 43 | let viewport; 44 | // TODO: clone item; 45 | if (!item.uri) { 46 | throw new Error(`${currentModule} missing location item.uri`); 47 | } 48 | 49 | // create viewport object 50 | viewport = { 51 | "width": !!Number(item.viewport.width) && typeof (item.viewport.width) !== "boolean" ? Number(item.viewport.width) : 1024, 52 | "height": !!Number(item.viewport.height) && typeof (item.viewport.height) !== "boolean" ? Number(item.viewport.height) : 786 53 | }; 54 | 55 | try { 56 | // remove regression item beforehand 57 | /* istanbul ignore if */ 58 | if (await fs.pathExists(item.regression_item) === true) { 59 | await fs.remove(item.regression_item); 60 | } 61 | 62 | // await fs.ensureDir(item.path); 63 | 64 | browser = await puppeteer.launch(); 65 | page = await browser.newPage(); 66 | await page.setViewport(viewport); 67 | await page.goto(item.uri); 68 | 69 | if (Number(item.wait) > 0) { 70 | await page.waitFor(Number(item.wait)); 71 | } 72 | 73 | await page.screenshot({ 74 | "fullPage": true, 75 | "path": item.regression_item 76 | }); 77 | await browser.close(); 78 | item.screenshot = true; 79 | return item; 80 | } catch (err) { 81 | console.log(`${currentModule} failed: Error ${err}`) 82 | throw new Error(`${currentModule} failed: Error ${err}`) 83 | // return false; 84 | } 85 | 86 | }; 87 | -------------------------------------------------------------------------------- /test/adapter/blink-diff.spec.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Specs for the blink-diff adapter 3 | * 4 | * Blink-Diff: "A lightweight image comparison tool http://yahoo.github.io/blink-diff/" 5 | * @see https://github.com/yahoo/blink-diff 6 | * 7 | * @copyright 2016, 2017 Martin Krause (http://martinkr.github.io) 8 | * @license MIT license: https://opensource.org/licenses/MIT 9 | * 10 | * @author Martin Krause 11 | */ 12 | 13 | /* eslint-env mocha */ 14 | 15 | const fs = require("fs-extra-plus"); 16 | const path = require("path"); 17 | const dataPath = path.join("./test/", "fixtures"); 18 | const crypto = require("crypto"); 19 | 20 | const thisModulePath = "adapter/blink-diff"; 21 | const thisModule = require("./../../app/" + thisModulePath); 22 | 23 | const port = 3000; 24 | const server = require("chigai-mock-server"); 25 | let testServer; 26 | 27 | /** creates a data-object as argument */ 28 | const createItem = () => { 29 | let item = { 30 | "viewport": {} 31 | }; 32 | item.uri = `http://localhost:${port}/static`; 33 | item.threshold = 0.01; 34 | item.viewport.width = 500; 35 | item.viewport.height = 500; 36 | item.hash = crypto.createHash("sha512").update(item.uri + item.viewport.width + item.viewport.height).digest("hex"); 37 | item.regression_item = path.join(dataPath, "first" + ".png"); 38 | item.reference_item = path.join(dataPath, "second" + ".png"); 39 | return item; 40 | }; 41 | 42 | 43 | 44 | describe(`the module ${thisModulePath}`, () => { 45 | 46 | afterEach((done) => { 47 | testServer.close(); 48 | done(); 49 | }); 50 | 51 | beforeEach(async () => { 52 | testServer = server.listen(port); 53 | }); 54 | 55 | after(async () => { 56 | await fs.emptyDir(path.join("./", "screenshots")); 57 | }); 58 | 59 | describe("should handle errors", () => { 60 | 61 | it("should catch blink-diff errors", (async () => { 62 | try { 63 | await thisModule((() => { 64 | let ret = createItem(); 65 | delete ret.regression_item; 66 | delete ret.reference_item; 67 | return ret; 68 | })()); 69 | } catch (error) { 70 | return error.should.be.an.instanceof(Error); 71 | } 72 | throw new Error("should throw"); 73 | })); 74 | 75 | 76 | }); 77 | 78 | describe("should work as expected", () => { 79 | 80 | it("should return false if the images differ", (async () => { 81 | let result; 82 | result = await thisModule((() => { 83 | let ret = createItem(); 84 | ret.regression_item = path.join(dataPath, "first" + ".png"); 85 | ret.reference_item = path.join(dataPath, "second" + ".png"); 86 | return ret; 87 | })()); 88 | result.match.should.not.be.ok; 89 | })); 90 | 91 | it("should return true if the images are the same", (async () => { 92 | let result; 93 | result = await thisModule((() => { 94 | let ret = createItem(); 95 | ret.regression_item = path.join(dataPath, "first" + ".png"); 96 | ret.reference_item = path.join(dataPath, "first" + ".png"); 97 | return ret; 98 | })()); 99 | result.match.should.be.ok; 100 | })); 101 | 102 | 103 | }); 104 | }); 105 | -------------------------------------------------------------------------------- /test/utils/data-object-wait.spec.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 3 | const fs = require("fs-extra-plus"); 4 | const path = require("path"); 5 | const crypto = require("crypto"); 6 | 7 | const thisModulePath = "data-object"; 8 | const thisModule = require("./../../app/utils/" + thisModulePath); 9 | 10 | 11 | const rcfile = path.join(process.cwd(), ".chigairc.json"); 12 | const customPath = "./test/_tmp/custom"; 13 | 14 | const defaultValue = 0; 15 | 16 | /** creates a data-object as argument */ 17 | const createItem = (uri, options) => { 18 | let item = { "viewport": {} }; 19 | item.uri = uri; 20 | item.options = options; 21 | return item; 22 | }; 23 | 24 | describe(`the module ${thisModulePath}`, () => { 25 | 26 | describe("should work as expected for the option \"wait\". It", () => { 27 | 28 | it("should return a data object with: a numeric wait-property", (async () => { 29 | let result; 30 | result = await thisModule(createItem("http://", { "wait": 500 })); 31 | result.wait.should.be.a("number"); 32 | })); 33 | 34 | it("should return a data object with: a given numeric wait-property, \"wait\" ", (async () => { 35 | let result; 36 | result = await thisModule(createItem("http://", { "wait": 500 })); 37 | result.wait.should.be.a("number").and.equal(500); 38 | })); 39 | 40 | it("should return a data object with: a default numeric wait-property if no \"wait\" is given", (async () => { 41 | let result; 42 | result = await thisModule(createItem("http://", { })); 43 | result.wait.should.be.a("number").and.equal(defaultValue); 44 | })); 45 | 46 | it("should return a data object with: a default numeric wait-property if \"wait\" is null", (async () => { 47 | let result; 48 | result = await thisModule(createItem("http://", { "wait": null })); 49 | result.wait.should.be.a("number").and.equal(defaultValue); 50 | })); 51 | 52 | it("should return a data object with: a default numeric wait-property if \"wait\" is undefined", (async () => { 53 | let result; 54 | result = await thisModule(createItem("http://", { "wait": undefined })); 55 | result.wait.should.be.a("number").and.equal(defaultValue); 56 | })); 57 | 58 | it("should return a data object with: a default numeric wait-property if \"wait\" is false", (async () => { 59 | let result; 60 | result = await thisModule(createItem("http://", { "wait": false })); 61 | result.wait.should.be.a("number").and.equal(defaultValue); 62 | })); 63 | 64 | it("should return a data object with: a default numeric wait-property if \"wait\" is true", (async () => { 65 | let result; 66 | result = await thisModule(createItem("http://", { "wait": true })); 67 | result.wait.should.be.a("number").and.equal(defaultValue); 68 | })); 69 | 70 | it("should return a data object with: a default numeric wait-property if \"wait\" is NaN", (async () => { 71 | let result; 72 | result = await thisModule(createItem("http://", { "wait": NaN })); 73 | result.wait.should.be.a("number").and.equal(defaultValue); 74 | })); 75 | 76 | it("should return a data object where the passed options take precedence over .chigairc.json for \"wait\"", (async () => { 77 | await fs.remove(rcfile); 78 | await fs.writeFile(rcfile, JSON.stringify({"wait": 99})); 79 | let result; 80 | let expectation = 999; 81 | let item = createItem("http://",{ "wait": expectation}); 82 | result = await thisModule(item); 83 | await fs.remove(rcfile); 84 | result.wait.should.equal(expectation); 85 | })); 86 | 87 | 88 | }); 89 | }); 90 | -------------------------------------------------------------------------------- /test/utils/data-object-vw.spec.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 3 | const fs = require("fs-extra-plus"); 4 | const path = require("path"); 5 | const crypto = require("crypto"); 6 | 7 | const thisModulePath = "data-object"; 8 | const thisModule = require("./../../app/utils/" + thisModulePath); 9 | 10 | 11 | const rcfile = path.join(process.cwd(), ".chigairc.json"); 12 | const customPath = "./test/_tmp/custom"; 13 | 14 | const defaultValue = 1024; 15 | 16 | /** creates a data-object as argument */ 17 | const createItem = (uri, options) => { 18 | let item = { "viewport": {} }; 19 | item.uri = uri; 20 | item.options = options; 21 | return item; 22 | }; 23 | 24 | describe(`the module ${thisModulePath}`, () => { 25 | 26 | describe("should work as expected for the option \"vw\". It", () => { 27 | 28 | it("should return a data object with: a numeric viewport width", (async () => { 29 | let result; 30 | result = await thisModule(createItem("http://", { "vw": 500 })); 31 | result.viewport.width.should.be.a("number"); 32 | })); 33 | 34 | it("should return a data object with: a given numeric viewport width, \"vw\" ", (async () => { 35 | let result; 36 | result = await thisModule(createItem("http://", { "vw": 500 })); 37 | result.viewport.width.should.be.a("number").and.equal(500); 38 | })); 39 | 40 | it("should return a data object with: a default numeric viewport width if no \"vw\" is given", (async () => { 41 | let result; 42 | result = await thisModule(createItem("http://", { })); 43 | result.viewport.width.should.be.a("number").and.equal(defaultValue); 44 | })); 45 | 46 | it("should return a data object with: a default numeric viewport width if \"vw\" is null", (async () => { 47 | let result; 48 | result = await thisModule(createItem("http://", { "vw": null })); 49 | result.viewport.width.should.be.a("number").and.equal(defaultValue); 50 | })); 51 | 52 | it("should return a data object with: a default numeric viewport width if \"vw\" is undefined", (async () => { 53 | let result; 54 | result = await thisModule(createItem("http://", { "vw": undefined })); 55 | result.viewport.width.should.be.a("number").and.equal(defaultValue); 56 | })); 57 | 58 | it("should return a data object with: a default numeric viewport width if \"vw\" is false", (async () => { 59 | let result; 60 | result = await thisModule(createItem("http://", { "vw": false })); 61 | result.viewport.width.should.be.a("number").and.equal(defaultValue); 62 | })); 63 | 64 | it("should return a data object with: a default numeric viewport width if \"vw\" is true", (async () => { 65 | let result; 66 | result = await thisModule(createItem("http://", { "vw": true })); 67 | result.viewport.width.should.be.a("number").and.equal(defaultValue); 68 | })); 69 | 70 | it("should return a data object with: a default numeric viewport width if \"vw\" is NaN", (async () => { 71 | let result; 72 | result = await thisModule(createItem("http://", { "vw": NaN })); 73 | result.viewport.width.should.be.a("number").and.equal(defaultValue); 74 | })); 75 | 76 | it("should return a data object where the passed options take precedence over .chigairc.json for \"vw\"", (async () => { 77 | await fs.remove(rcfile); 78 | await fs.writeFile(rcfile, JSON.stringify({"vw": 99})); 79 | let result; 80 | let expectation = 999; 81 | let item = createItem("http://",{ "vw": expectation}); 82 | result = await thisModule(item); 83 | await fs.remove(rcfile); 84 | result.viewport.width.should.equal(expectation); 85 | })); 86 | 87 | }); 88 | }); 89 | -------------------------------------------------------------------------------- /test/utils/data-object-vh.spec.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 3 | const fs = require("fs-extra-plus"); 4 | const path = require("path"); 5 | const crypto = require("crypto"); 6 | 7 | const thisModulePath = "data-object"; 8 | const thisModule = require("./../../app/utils/" + thisModulePath); 9 | 10 | const defaultValue = 786; 11 | 12 | const rcfile = path.join(process.cwd(), ".chigairc.json"); 13 | const customPath = "./test/_tmp/custom"; 14 | 15 | /** creates a data-object as argument */ 16 | const createItem = (uri, options) => { 17 | let item = { "viewport": {} }; 18 | item.uri = uri; 19 | item.options = options; 20 | return item; 21 | }; 22 | 23 | describe(`the module ${thisModulePath}`, () => { 24 | 25 | describe("should work as expected for the option \"vh\". It", () => { 26 | 27 | it("should return a data object with: a numeric viewport height", (async () => { 28 | let result; 29 | result = await thisModule(createItem("http://", { "vh": 500 })); 30 | result.viewport.height.should.be.a("number"); 31 | })); 32 | 33 | it("should return a data object with: a given numeric viewport height, \"vh\" ", (async () => { 34 | let result; 35 | result = await thisModule(createItem("http://", { "vh": 500 })); 36 | result.viewport.height.should.be.a("number").and.equal(500); 37 | })); 38 | 39 | it("should return a data object with: a default numeric viewport height if no \"vh\" is given", (async () => { 40 | let result; 41 | result = await thisModule(createItem("http://", { })); 42 | result.viewport.height.should.be.a("number").and.equal(defaultValue); 43 | })); 44 | 45 | it("should return a data object with: a default numeric viewport height if \"vh\" is null", (async () => { 46 | let result; 47 | result = await thisModule(createItem("http://", { "vh": null })); 48 | result.viewport.height.should.be.a("number").and.equal(defaultValue); 49 | })); 50 | 51 | it("should return a data object with: a default numeric viewport height if \"vh\" is undefined", (async () => { 52 | let result; 53 | result = await thisModule(createItem("http://", { "vh": undefined })); 54 | result.viewport.height.should.be.a("number").and.equal(defaultValue); 55 | })); 56 | 57 | it("should return a data object with: a default numeric viewport height if \"vh\" is false", (async () => { 58 | let result; 59 | result = await thisModule(createItem("http://", { "vh": false })); 60 | result.viewport.height.should.be.a("number").and.equal(defaultValue); 61 | })); 62 | 63 | it("should return a data object with: a default numeric viewport height if \"vh\" is true", (async () => { 64 | let result; 65 | result = await thisModule(createItem("http://", { "vh": true })); 66 | result.viewport.height.should.be.a("number").and.equal(defaultValue); 67 | })); 68 | 69 | it("should return a data object with: a default numeric viewport height if \"vh\" is NaN", (async () => { 70 | let result; 71 | result = await thisModule(createItem("http://", { "vh": NaN })); 72 | result.viewport.height.should.be.a("number").and.equal(defaultValue); 73 | })); 74 | 75 | it("should return a data object where the passed options take precedence over .chigairc.json for \"vh\"", (async () => { 76 | await fs.remove(rcfile); 77 | await fs.writeFile(rcfile, JSON.stringify({"vh": 99})); 78 | let result; 79 | let expectation = 999; 80 | let item = createItem("http://",{ "vh": expectation}); 81 | result = await thisModule(item); 82 | await fs.remove(rcfile); 83 | result.viewport.height.should.equal(expectation); 84 | })); 85 | 86 | 87 | }); 88 | }); 89 | -------------------------------------------------------------------------------- /test/utils/data-object-threshold.spec.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 3 | const fs = require("fs-extra-plus"); 4 | const path = require("path"); 5 | const crypto = require("crypto"); 6 | 7 | const thisModulePath = "data-object"; 8 | const thisModule = require("./../../app/utils/" + thisModulePath); 9 | 10 | const defaultValue = 0.01; 11 | 12 | const rcfile = path.join(process.cwd(), ".chigairc.json"); 13 | const customPath = "./test/_tmp/custom"; 14 | 15 | /** creates a data-object as argument */ 16 | const createItem = (uri, options) => { 17 | let item = { "viewport": {} }; 18 | item.uri = uri; 19 | item.options = options; 20 | return item; 21 | }; 22 | 23 | describe(`the module ${thisModulePath}`, () => { 24 | 25 | describe("should work as expected for the option \"path\". It", () => { 26 | 27 | it("should return a data object with: a numeric threshold", (async () => { 28 | let result; 29 | result = await thisModule(createItem("http://", {})); 30 | result.threshold.should.be.a("number"); 31 | })); 32 | 33 | it("should return a data object with: a given numeric threshold, \"threshold\"", (async () => { 34 | let result; 35 | result = await thisModule(createItem("http://", { "threshold" : 10})); 36 | result.threshold.should.be.a("number").and.equal(10); 37 | })); 38 | 39 | it("should return a data object with: a default numeric threshold if no \"threshold\" is given", (async () => { 40 | let result; 41 | result = await thisModule(createItem("http://", { })); 42 | result.threshold.should.be.a("number").and.equal(defaultValue); 43 | })); 44 | 45 | it("should return a data object with: a default numeric threshold width if \"threshold\" is null", (async () => { 46 | let result; 47 | result = await thisModule(createItem("http://", { "threshold": null })); 48 | result.threshold.should.be.a("number").and.equal(defaultValue); 49 | })); 50 | 51 | it("should return a data object with: a default numeric threshold width if \"threshold\" is undefined", (async () => { 52 | let result; 53 | result = await thisModule(createItem("http://", { "threshold": undefined })); 54 | result.threshold.should.be.a("number").and.equal(defaultValue); 55 | })); 56 | 57 | it("should return a data object with: a default numeric threshold if \"threshold\" is false", (async () => { 58 | let result; 59 | result = await thisModule(createItem("http://", { "threshold": false })); 60 | result.threshold.should.be.a("number").and.equal(defaultValue); 61 | })); 62 | 63 | it("should return a data object with: a default numeric threshold if \"threshold\" is true", (async () => { 64 | let result; 65 | result = await thisModule(createItem("http://", { "threshold": true })); 66 | result.threshold.should.be.a("number").and.equal(defaultValue); 67 | })); 68 | 69 | it("should return a data object with: a default numeric threshold if \"threshold\" is NaN", (async () => { 70 | let result; 71 | result = await thisModule(createItem("http://", { "threshold": NaN })); 72 | result.threshold.should.be.a("number").and.equal(defaultValue); 73 | })); 74 | 75 | it("should return a data object where the passed options take precedence over .chigairc.json for \"threshold\"", (async () => { 76 | await fs.remove(rcfile); 77 | await fs.writeFile(rcfile, JSON.stringify({"threshold": 0.9})); 78 | let result; 79 | let expectation = 0.99; 80 | let item = createItem("http://",{ "threshold": expectation}); 81 | result = await thisModule(item); 82 | await fs.remove(rcfile); 83 | result.threshold.should.equal(expectation); 84 | })); 85 | 86 | }); 87 | }); 88 | -------------------------------------------------------------------------------- /test/utils/data-object.spec.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 3 | const fs = require("fs-extra-plus"); 4 | const path = require("path"); 5 | const crypto = require("crypto"); 6 | 7 | const thisModulePath = "data-object"; 8 | const thisModule = require("./../../app/utils/" + thisModulePath); 9 | 10 | 11 | const rcfile = path.join(process.cwd(), ".chigairc.json"); 12 | const customPath = "./test/_tmp/custom"; 13 | 14 | /** creates a data-object as argument */ 15 | const createItem = (uri, options) => { 16 | let item = { "viewport": {} }; 17 | item.uri = uri; 18 | item.options = options; 19 | return item; 20 | }; 21 | 22 | describe(`the module ${thisModulePath}`, () => { 23 | 24 | describe("should rely on all arguments and handle all errors", () => { 25 | 26 | it("should throw if there's no first argument (location)", (async() => { 27 | try { 28 | await thisModule(createItem(null, {})); 29 | } catch (error) { 30 | return error.should.be.an.instanceof(Error); 31 | } 32 | throw new Error("should throw"); 33 | })); 34 | 35 | it("should throw if the first argument is not a string", (async () => { 36 | try { 37 | await thisModule(createItem([], {})); 38 | } catch (error) { 39 | return error.should.be.an.instanceof(Error); 40 | } 41 | throw new Error("should throw"); 42 | })); 43 | 44 | it("should use a default value for \"options\" if there's no second argument", (async() => { 45 | let result; 46 | result = await thisModule(createItem("http://")); 47 | result.should.be.an("object"); 48 | })); 49 | 50 | 51 | }); 52 | 53 | describe("should work as expected", () => { 54 | 55 | it("should return an object", (async() => { 56 | let result; 57 | result = await thisModule(createItem("http://", {})); 58 | result.should.be.an("object"); 59 | })); 60 | 61 | 62 | it("should return a data object with: an uri", (async() => { 63 | let result; 64 | result = await thisModule(createItem("http://", {})); 65 | result.uri.should.be.a("string"); 66 | })); 67 | 68 | it("should return a data object with: a timestamp_iso", (async () => { 69 | let result; 70 | result = await thisModule(createItem("http://", {})); 71 | result.timestamp_iso.should.be.a("string"); 72 | })); 73 | 74 | it("should return a data object with: a hash", (async () => { 75 | let result; 76 | result = await thisModule(createItem("http://", {})); 77 | result.hash.should.be.a("string"); 78 | })); 79 | 80 | it("should return a data object with: a hash based on uri, vw, vh", (async () => { 81 | let result; 82 | result = await thisModule(createItem("http://", {"vw": 100, "vh": 200, "wait": 100 })); 83 | result.hash.should.equal( (() => crypto.createHash("sha512").update("http://" + 100 + 200 + 100).digest("hex"))()); 84 | })); 85 | 86 | it("should return a data object with: a path for the screenshots", (async () => { 87 | let result; 88 | result = await thisModule(createItem("http://", {})); 89 | result.path.should.be.a("string"); 90 | })); 91 | 92 | it("should return a data object with: a numeric threshold", (async () => { 93 | let result; 94 | result = await thisModule(createItem("http://", {})); 95 | result.threshold.should.be.a("number"); 96 | })); 97 | 98 | it("should return a data object with: a numeric viewport width", (async () => { 99 | let result; 100 | result = await thisModule(createItem("http://", { "vw": 500 })); 101 | result.viewport.width.should.be.a("number"); 102 | })); 103 | 104 | it("should return a data object with: a numeric viewport height", (async () => { 105 | let result; 106 | result = await thisModule(createItem("http://", { "vh": 500 })); 107 | result.viewport.height.should.be.a("number"); 108 | })); 109 | 110 | 111 | }); 112 | }); 113 | -------------------------------------------------------------------------------- /test/utils/reference-item.spec.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Specs for the reference-item utility module 3 | * This module ensures the existence of a reference item. 4 | * It creates a new reference item for the comparison if none exists. 5 | * The fresh reference item is just a copy of the regression item. 6 | * It sets the flag "fresh" on the data-object: 7 | * - true if it had to create a fresh reference item 8 | * - false if the reference item already existed 9 | * 10 | * @copyright 2016, 2017 Martin Krause (http://martinkr.github.io) 11 | * @license MIT license: https://opensource.org/licenses/MIT 12 | * 13 | * @author Martin Krause 14 | */ 15 | 16 | /* eslint-env mocha */ 17 | const fs = require("fs-extra-plus"); 18 | const path = require("path"); 19 | const crypto = require("crypto"); 20 | 21 | const thisModulePath = "utils/reference-item"; 22 | const thisModule = require("./../../app/" + thisModulePath); 23 | 24 | const dataPath = path.join("./test/", "data"); 25 | 26 | 27 | // setup data 28 | 29 | const hash = crypto.createHash("sha512").update("http://rebel-mother.surge.sh" + 1024 + 786).digest("hex"); 30 | const regressionItem = path.join("./", "screenshots", hash + "_regression.png"); 31 | const referenceItem = path.join("./", "screenshots", hash + "_reference.png"); 32 | 33 | 34 | /** creates a data-object as argument */ 35 | const createItem = () => { 36 | let item = { "viewport": {} }; 37 | item.uri = "http://rebel-mother.surge.sh"; 38 | item.viewport.width = 500; 39 | item.viewport.height = 500; 40 | item.hash = hash; 41 | item.regression_item = regressionItem; 42 | item.reference_item = referenceItem; 43 | return item; 44 | }; 45 | 46 | describe(`the module ${thisModulePath}`, () => { 47 | 48 | beforeEach(async () => { 49 | await fs.emptyDir(dataPath); 50 | await fs.ensureFile(regressionItem); 51 | await fs.ensureFile(referenceItem); 52 | }); 53 | 54 | 55 | after(async () => { 56 | await fs.emptyDir(path.join("./", "screenshots")); 57 | }); 58 | 59 | describe("should rely on all arguments and handle all errors", () => { 60 | 61 | it("should throw if theres no argument ", (async () => { 62 | try { 63 | await thisModule(null); 64 | } catch (error) { 65 | return error.should.be.an.instanceof(Error); 66 | } 67 | throw new Error("should throw"); 68 | })); 69 | 70 | it("should throw if the argument is not an object", (async () => { 71 | try { 72 | await thisModule([{}]); 73 | } catch (error) { 74 | return error.should.be.an.instanceof(Error); 75 | } 76 | throw new Error("should throw"); 77 | })); 78 | 79 | it("should throw if the regression_item does not exists", (async () => { 80 | try { 81 | await fs.remove(regressionItem); 82 | await thisModule(createItem()); 83 | } catch (error) { 84 | return error.should.be.an.instanceof(Error); 85 | } 86 | throw new Error("should throw"); 87 | })); 88 | 89 | }); 90 | 91 | describe("should work as expected", () => { 92 | 93 | it("should return an object", (async () => { 94 | let result; 95 | result = await thisModule(createItem()); 96 | result.should.be.an("object"); 97 | })); 98 | 99 | it("should create a regression item if none exists", (async () => { 100 | let result; 101 | let exists; 102 | await fs.remove(referenceItem); 103 | result = await thisModule(createItem()); 104 | exists = await fs.pathExists(result.regression_item); 105 | exists.should.be.ok; 106 | })); 107 | 108 | it("should set a flag if there was no regression_item", (async () => { 109 | let result; 110 | let exists; 111 | await fs.remove(referenceItem); 112 | result = await thisModule(createItem()); 113 | result.fresh.should.be.ok; 114 | })); 115 | 116 | it("should be no flag if there was a regression_item", (async () => { 117 | let result; 118 | await fs.ensureFile(regressionItem); 119 | result = await thisModule(createItem()); 120 | result.fresh.should.not.be.ok; 121 | })); 122 | 123 | }); 124 | }); 125 | -------------------------------------------------------------------------------- /test/api/fresh-reference.spec.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Specs for the fresh-reference module. 4 | * Creates a fresh reference for a given set of items. 5 | * It ensures the arguments have the correct form. 6 | * It creates the data-objects. 7 | * It removes the current reference item. 8 | * It creates a new screenshot (regression item). 9 | * It sets the freshly created screenshot as regression item. 10 | * It sets the "fresh" flag to true. 11 | * 12 | * The specs use stubs for all dependencies. 13 | * There are specs for the dependend modules. 14 | * 15 | * @copyright 2017 Martin Krause (http://martinkr.github.io) 16 | * @license MIT license: https://opensource.org/licenses/MIT 17 | * 18 | * @author Martin Krause 19 | */ 20 | 21 | /* eslint-env mocha */ 22 | const fs = require("fs-extra-plus"); 23 | const path = require("path"); 24 | const crypto = require("crypto"); 25 | const proxyquire = require("proxyquire").noCallThru(); 26 | 27 | const thisModulePath = "api/fresh-reference"; 28 | // const thisModule = require("./../../app/" + thisModulePath); 29 | let thisModule; 30 | 31 | const port = 3000; 32 | const server = require("chigai-mock-server"); 33 | let testServer; 34 | 35 | 36 | // mock dependencies 37 | const stubAndReturn = ((value) => { 38 | thisModule = proxyquire("./../../app/" + thisModulePath, 39 | { 40 | "./../adapter/puppeteer": (item) => { return new Promise((resolve, reject) => {item.screenshot = value; resolve(item); }); }, 41 | "./../utils/reference-item": (item) => { return new Promise((resolve, reject) => { item.fresh = value; resolve(item); }); } 42 | } 43 | ); 44 | }); 45 | 46 | // setup data 47 | const dataPathDefault = path.join("./", "screenshots"); 48 | const dataPathCustom = path.join("./", "/test/_tmp/customfolder"); 49 | const hash = crypto.createHash("sha512").update(`http://localhost:${port}/static` + 1024 + 786).digest("hex"); 50 | const regressionItem = path.join("./", "screenshots", hash + "_regression.png"); 51 | const referenceItem = path.join("./", "screenshots", hash + "_reference.png"); 52 | 53 | /** creates a data-object as argument */ 54 | const createItem = () => { 55 | let item = { "viewport": {} }; 56 | item.uri = `http://localhost:${port}/static`; 57 | item.hash = hash; 58 | item.options = {}; 59 | return item; 60 | }; 61 | 62 | describe(`the module ${thisModulePath}`, () => { 63 | 64 | afterEach((done) => { 65 | testServer.close(); 66 | done(); 67 | }); 68 | 69 | beforeEach(async () => { 70 | testServer = server.listen(port); 71 | await fs.remove(dataPathCustom); 72 | await fs.emptyDir(dataPathDefault); 73 | await fs.ensureFile(regressionItem); 74 | await fs.ensureFile(referenceItem); 75 | stubAndReturn(true); 76 | }) 77 | 78 | after(async() => { 79 | await fs.emptyDir(path.join("./", "screenshots")); 80 | }); 81 | 82 | describe("should handle errors", () => { 83 | 84 | it("should throw if there's no argument", (async() => { 85 | try { 86 | await thisModule(null); 87 | } catch (error) { 88 | return error.should.be.an.instanceof(Error); 89 | } 90 | 91 | throw new Error("should throw"); 92 | })); 93 | 94 | it("should throw if there's no array as argument", (async () => { 95 | try { 96 | await thisModule({}); 97 | } catch (error) { 98 | return error.should.be.an.instanceof(Error); 99 | } 100 | throw new Error("should throw"); 101 | })); 102 | 103 | it("should throw if there's no propery \"uri\" in the object", (async() => { 104 | try { 105 | await thisModule([{"foo": "bar"}]); 106 | } catch (error) { 107 | return error.should.be.an.instanceof(Error); 108 | } 109 | throw new Error("should throw"); 110 | })); 111 | 112 | }); 113 | 114 | describe("should work as expected", () => { 115 | 116 | it("should ensure the screenshot directory is available", (async() => { 117 | let result; 118 | let item = createItem(); 119 | item.options.path = dataPathCustom; 120 | // stubAndReturn(true); 121 | result = await thisModule([item]); 122 | result = await fs.pathExists(dataPathCustom); 123 | result.should.be.ok; 124 | })); 125 | 126 | it("should return an array", (async() => { 127 | let result; 128 | // stubAndReturn(true); 129 | result = await thisModule([createItem()]); 130 | result.should.be.an("array"); 131 | })); 132 | 133 | it("should return one result if one item was given", (async() => { 134 | let result; 135 | // stubAndReturn(true); 136 | result = await thisModule([createItem()]); 137 | result.should.have.a.lengthOf(1); 138 | })); 139 | 140 | it("should return two results if two items were given", (async() => { 141 | let result; 142 | // stubAndReturn(true); 143 | result = await thisModule([createItem(),createItem()]); 144 | result.should.have.a.lengthOf(2); 145 | })); 146 | 147 | it("should set \"fresh\" to true", (async() => { 148 | let result; 149 | // stubAndReturn(true); 150 | result = await thisModule([createItem()]); 151 | (result.shift().fresh).should.be.ok 152 | })); 153 | 154 | }); 155 | }); 156 | -------------------------------------------------------------------------------- /test/adapter/puppeteer.spec.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Specs for the puppeteer adapter 3 | * This module integrates chrome's headless node API 4 | * It makes screenshots from the given uris and saves them as PNG files. 5 | * It creates the PNG used as regression item. 6 | * It sets the flag "screenshot" on the data-object: 7 | * - true if the regression item was created successfully 8 | * - false if the regression item was not created successfully 9 | * 10 | * 11 | * "Puppeteer is a Node library which provides a high-level API to control headless Chrome over the DevTools Protocol." 12 | * @see https://github.com/GoogleChrome/puppeteer 13 | * 14 | * @copyright 2017 Martin Krause (http://martinkr.github.io) 15 | * @license MIT license: https://opensource.org/licenses/MIT 16 | * 17 | * @author Martin Krause 18 | */ 19 | 20 | /* eslint-env mocha */ 21 | 22 | const fs = require("fs-extra-plus"); 23 | const path = require("path"); 24 | 25 | const crypto = require("crypto"); 26 | 27 | const thisModulePath = "adapter/puppeteer"; 28 | const thisModule = require("./../../app/" + thisModulePath); 29 | const imageSize = require("image-size"); 30 | 31 | const port = 3000; 32 | const server = require("chigai-mock-server"); 33 | let testServer; 34 | const defaultWidth = 550; 35 | const defaultHeight = 500; 36 | 37 | /** creates a data-object as argument */ 38 | const createItem = (object = {}) => { 39 | let item = { 40 | "viewport": {} 41 | }; 42 | item.wait = object.wait || 0; 43 | item.uri = `http://localhost:${port}/static`; 44 | item.viewport = {}; 45 | item.viewport.width = object.width || defaultWidth; 46 | item.viewport.height = object.height || defaultHeight; 47 | item.hash = crypto.createHash("sha512").update(item.uri + item.viewport.width + item.viewport.height).digest("hex"); 48 | item.regression_item = path.join("./", "screenshots", item.hash + "_regression.png"); 49 | return item; 50 | }; 51 | 52 | 53 | describe(`the module ${thisModulePath}`, () => { 54 | 55 | afterEach((done) => { 56 | testServer.close(); 57 | done(); 58 | }); 59 | 60 | beforeEach(async () => { 61 | testServer = server.listen(port); 62 | await fs.emptyDir(path.join("./", "screenshots")); 63 | }); 64 | 65 | after(async () => { 66 | await fs.emptyDir(path.join("./", "screenshots")); 67 | }); 68 | 69 | describe("should handle errors", () => { 70 | 71 | it("should throw if there's no location \"item.uri\"", (async () => { 72 | try { 73 | await thisModule((() => { 74 | let ret = createItem(); 75 | delete ret.uri; 76 | return ret; 77 | })()); 78 | } catch (error) { 79 | return error.should.be.an.instanceof(Error); 80 | } 81 | throw new Error("should throw"); 82 | })); 83 | 84 | it("should throw if there's an error in puppeteer", (async () => { 85 | try { 86 | await thisModule((() => { 87 | let ret = createItem(); 88 | ret.uri = "Invalid"; 89 | return ret; 90 | })()); 91 | } catch (error) { 92 | return error.should.be.an.instanceof(Error); 93 | } 94 | })); 95 | 96 | }); 97 | 98 | describe("should work as expected", () => { 99 | 100 | it("should create a screenshot based on the item", (async () => { 101 | let result; 102 | result = await thisModule(createItem()); 103 | result.screenshot.should.be.ok; 104 | })); 105 | 106 | it("should wait and create a screenshot based on the item", (async () => { 107 | let time = Date.now(); 108 | let wait = 5000; 109 | await thisModule(createItem({ 110 | "wait": wait 111 | })); 112 | (Date.now() - time).should.be.above(wait - 1); 113 | })); 114 | 115 | it("should create a screenshot with the default height", (async () => { 116 | let result; 117 | result = await thisModule(createItem()); 118 | result = imageSize(result.regression_item); 119 | result.height.should.equal(defaultHeight); 120 | })); 121 | 122 | it("should create a screenshot with the default width", (async () => { 123 | let result; 124 | result = await thisModule(createItem()); 125 | result = imageSize(result.regression_item); 126 | result.width.should.equal(defaultWidth); 127 | })); 128 | 129 | it("should create a screenshot with the supplied width", (async () => { 130 | let result; 131 | let _width = 300; 132 | result = await thisModule(createItem({ 133 | "width": _width 134 | })); 135 | result = imageSize(result.regression_item); 136 | result.width.should.equal(_width); 137 | })); 138 | 139 | 140 | it("should create a screenshot with the supplied height", (async () => { 141 | let result; 142 | let _height = 400; 143 | result = await thisModule(createItem({ 144 | "height": _height 145 | })); 146 | result = imageSize(result.regression_item); 147 | result.height.should.equal(_height); 148 | })); 149 | 150 | 151 | it("should save the screenshot properly", (async () => { 152 | let result; 153 | result = await thisModule(createItem()); 154 | result = await fs.pathExists(result.regression_item); 155 | result.should.be.ok; 156 | })); 157 | 158 | it("should save the screenshot even if theres no viewport specifiec", (async () => { 159 | let result; 160 | result = await thisModule((() => { 161 | let ret = createItem(); 162 | delete ret.viewport.width; 163 | delete ret.viewport.height; 164 | return ret; 165 | })()); 166 | result = await fs.pathExists(result.regression_item); 167 | result.should.be.ok; 168 | })); 169 | 170 | }); 171 | }); 172 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # chigai-core [![Build Status](https://travis-ci.org/martinkr/chigai-core.svg?branch=master)](https://travis-ci.org/martinkr/chigai-core) 2 | Modern visual regression testing with ```puppeteer``` and ```blink-diff``` 3 | 4 | ## Visual regression testing 5 | Visual regression testing: compare your current state against your baseline. 6 | It's nothing new. We've done this for ages. Comparing two images. Looking at the current version and the previous one. Searching for the difference. On our own. Manually. 7 | 8 | Well, Chiagi does this automatically. It takes a screenshot from a given URI and compares it to the previous version. 9 | 10 | ## Automated. Always 11 | Chigai is designed to be used in your tests. You can use it in your favourite testrunner. As a regression tool. If your layout changes, it won't go on unnoticed. 12 | 13 | ## How to use this? 14 | Chigai-Core provides the core functionalities: The regression testing of a given url and creating a fresh reference item beforehand. 15 | Chigai-core is not meant to be used directly. Use the CLI or the API modules. They provide a clean facade for the core functions. 16 | 17 | ## Regression testing 18 | Provide a url and options such as the viewport width (default: 1024), viewport height (default: 720) or a threshold (default: 0.01 = 1%) for the image comparison. Chigai-core creates a new screenshot of the whole page and compares it to the last specified reference. If their difference is lower than the given threshold, ```match```will be ```true```. Otherwise, ```match``` becomes ```false```. 19 | 20 | Chigai creates a unique identifier from the url, the viewport size and the timeout. 21 | 22 | ## API 23 | 24 | Chigai-Core provides two public APIs: 25 | 26 | ### ```regression(uri, options)``` 27 | Handles the regression test for an items. Each item requires a property "uri" and can have an optional set of options. It returns an ```async``` function and finaly a return object. 28 | 29 | ### ```reference(uri, options)``` 30 | Creates a new reference for an item. Each item requires a property "uri" and can have an optional set of options. It returns an ```async``` function and finaly a return object. 31 | 32 | ### Configuration options 33 | Options can either be passed as arguments per call or globally via .chigairs.json file. The options-object takes precedence. 34 | 35 | #### ```vw``` 36 | Default: ```1024```. The with of the viewport you cant to screenshot. This will be part of the unique identifier. 37 | #### ```vh``` 38 | Default: ```786```. The height of the viewport you cant to screenshot. This will be part of the unique identifier. 39 | 40 | #### ```threshold``` 41 | Default: ```0.01```. The threshold to use for the comparison. This will not be part of the unique identifier. 42 | 43 | #### ```path``` 44 | Default: ```./screenshots``` There's the possiblitly to pass a custom path to chigai. It's relative to your working directory. 45 | Use this to share your reference items (e.g. via source control, rsync ...). 46 | 47 | #### ```wait``` 48 | Default: ```0```. Wait this amount of miliseconds after the page's ```load-event``` before making the screenshot. This will not be part of the unique identifier. 49 | 50 | #### .chigai.json 51 | You can store project wide setttings in this file. It takes the same key-value-pairs as the options-object. Plus an additional ```path``` property. 52 | 53 | 54 | ```JavaScript 55 | { 56 | "path" : "./myscreenshots", 57 | "threshold": 0.5, 58 | "vw": 1200, 59 | "vh" : 800, 60 | "wait": 5000 61 | } 62 | ``` 63 | 64 | ### Return Object 65 | Each API returns an object with the following properties: 66 | 67 | #### ```viewport``` 68 | An object with two properties, ```width``` and ```height```, indicating the viewport size of the screenshot. 69 | #### ```uri``` 70 | The screnshotted location 71 | #### ```threshold``` 72 | The threshold used for the comparison 73 | #### ```wait``` 74 | The delay between ```load``` and screenshotting 75 | #### ```path``` 76 | The path to the screenshots 77 | #### ```timestamp_iso``` 78 | An ISO-Timestamp of the screenshot 79 | #### ```hash``` 80 | The unique identifier of this screenshot. Based on the `uri`, `viewport width`, `viewport height` and `wait` 81 | #### ```regression_item``` 82 | The path to the regression item. The latest screenshot of this location. 83 | #### ```reference_item``` 84 | The path to the reference item. The reference screenshot of this location. 85 | #### ```difference_item``` 86 | The path to an image showing the difference between the reference and the regression item. 87 | #### ```match``` 88 | Boolean, true if the difference is below the threshold, aka "success". 89 | #### ```screenshot``` 90 | Boolean, true if the screenshotting of the current location was successful. 91 | #### ```fresh``` 92 | Boolean, true if this pass resulted in a fresh reference item: none available or forced. 93 | 94 | 95 | ## Fresh reference 96 | If you know you changed the layout, just set a new reference item before running the tests again. 97 | 98 | ## Installation 99 | ```$ yarn add chigai-core``` 100 | 101 | ## Example 102 | Chigai-core is not meant to be used directly. Use the CLI or the API modules. They provide a clean facade for the core functions. 103 | 104 | ## Tech Stack 105 | - ECMAScript 2018 on ```nodejs v8.5.0``` 106 | - ```blink-diff v1.0.1``` 107 | - ```fs-extra-plus v0.1.3``` 108 | - ```puppeteer v0.11.0``` 109 | - 100% code coverage using ```mocha v3.5.2```, ```chai v4.1.2``` and ```nyc v11.2.1```, 110 | 111 | ## Resources 112 | - [GoogleChrome/puppeteer - Headless Chrome Node API](https://github.com/GoogleChrome/puppeteer) 113 | - [yahoo/blink-diff - A lightweight image comparison tool](https://github.com/yahoo/blink-diff) 114 | 115 | ## License 116 | Licensed under the [MIT license](http://www.opensource.org/licenses/mit-license.php). 117 | 118 | Copyright (c) 2016, 2017 Martin Krause [http://martinkr.github.io)](http://martinkr.github.io) 119 | -------------------------------------------------------------------------------- /test/utils/config-file.spec.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Specs for the config-file utility module 4 | * This module checks for a .chigailrc.json file. 5 | * It provides an API for getting config options 6 | * and makes sure all modules can work with the options 7 | * 8 | * @copyright 2016, 2017 Martin Krause (http://martinkr.github.io) 9 | * @license MIT license: https://opensource.org/licenses/MIT 10 | * 11 | * @author Martin Krause 12 | */ 13 | 14 | /* eslint-env mocha */ 15 | const fs = require("fs-extra-plus"); 16 | const path = require("path"); 17 | 18 | 19 | const thisModulePath = "utils/config-file"; 20 | const thisModule = require("./../../app/" + thisModulePath); 21 | 22 | const rcfile = path.join(process.cwd(), ".chigairc.json"); 23 | const defaults = { "path": path.join(process.cwd(), "./screenshots") , "vw": 1024,"vh": 786, "threshold": 0.01, "wait": 0}; 24 | const customPath = "./test/_tmp/custom"; 25 | const customVW = 200; 26 | const customVH = 300; 27 | const customThreshold = 1; 28 | const customWait = 9999; 29 | 30 | const rcfileOptions = { "path": path.join(process.cwd(), customPath) }; 31 | 32 | 33 | // setup data 34 | 35 | describe(`the module ${thisModulePath}`, () => { 36 | 37 | before(async() => { 38 | }); 39 | 40 | 41 | after(async() => { 42 | }); 43 | 44 | describe("should return the default options if no rcfile is present", () => { 45 | beforeEach(async () => { 46 | await fs.remove(rcfile); 47 | }); 48 | 49 | it("should return the label for default ", (async () => { 50 | let result = await thisModule(); 51 | result.from.should.equal("default"); 52 | })); 53 | 54 | it("should return the default path ", (async () => { 55 | let result = await thisModule(); 56 | result.path.should.equal(defaults.path); 57 | })); 58 | 59 | }); 60 | 61 | 62 | describe("should return the default options if the rcfile is invalid", () => { 63 | beforeEach(async () => { 64 | await fs.remove(rcfile); 65 | await fs.writeFile(rcfile, "{invalid:json}"); 66 | }); 67 | 68 | after(async() => { 69 | await fs.remove(rcfile); 70 | }); 71 | 72 | it("should return the label for default ", (async () => { 73 | let result = await thisModule(); 74 | result.from.should.equal("default"); 75 | })); 76 | 77 | it("should return the default path ", (async () => { 78 | let result = await thisModule(); 79 | result.path.should.equal(defaults.path); 80 | })); 81 | 82 | }); 83 | 84 | describe("should return the default options for a key if this key is missing", () => { 85 | beforeEach(async () => { 86 | await fs.remove(rcfile); 87 | }); 88 | 89 | after(async() => { 90 | await fs.remove(rcfile); 91 | }); 92 | 93 | it("should return the label for the .chigairc.json file ", (async () => { 94 | await fs.writeFile(rcfile, JSON.stringify({"x": true})); 95 | let result = await thisModule(); 96 | result.from.should.have.a.string(".chigairc.json"); 97 | })); 98 | 99 | it("should return the default path if the path is missing ", (async () => { 100 | await fs.writeFile(rcfile, JSON.stringify({"x": true})); 101 | let result = await thisModule(); 102 | result.path.should.equal(defaults.path); 103 | })); 104 | 105 | it("should return the default vh if the vh is missing ", (async () => { 106 | await fs.writeFile(rcfile, JSON.stringify({"x": true})); 107 | let result = await thisModule(); 108 | result.vh.should.equal(defaults.vh); 109 | })); 110 | 111 | it("should return the default vw if the vw is missing ", (async () => { 112 | await fs.writeFile(rcfile, JSON.stringify({"x": true})); 113 | let result = await thisModule(); 114 | result.vw.should.equal(defaults.vw); 115 | })); 116 | 117 | it("should return the default threshold if the threshold is missing ", (async () => { 118 | await fs.writeFile(rcfile, JSON.stringify({"x": true})); 119 | let result = await thisModule(); 120 | result.threshold.should.equal(defaults.threshold); 121 | })); 122 | 123 | it("should return the default wait-value if the wait-value is missing ", (async () => { 124 | await fs.writeFile(rcfile, JSON.stringify({"x": true})); 125 | let result = await thisModule(); 126 | result.threshold.should.equal(defaults.threshold); 127 | })); 128 | 129 | }); 130 | describe("should work as expected and it", () => { 131 | 132 | beforeEach(async () => { 133 | await fs.remove(rcfile); 134 | await fs.writeFile(rcfile, JSON.stringify({"path": customPath, "vw": customVW, "vh": customVH, "threshold": customThreshold, "wait": customWait})); 135 | }); 136 | 137 | after(async() => { 138 | await fs.remove(rcfile); 139 | }); 140 | 141 | it("should return the label for the .chigairc.json file ", (async () => { 142 | let result = await thisModule(); 143 | result.from.should.have.a.string(".chigairc.json"); 144 | })); 145 | 146 | it("should return the custom path ", (async () => { 147 | let result = await thisModule(); 148 | result.path.should.equal(rcfileOptions.path); 149 | })); 150 | 151 | it("should create the custom path if it's not already there", (async () => { 152 | let result; 153 | await fs.remove(rcfileOptions.path); 154 | await thisModule(); 155 | result = await fs.pathExists(rcfileOptions.path); 156 | result.should.be.ok; 157 | })); 158 | 159 | it("should return the custom vw", (async () => { 160 | let result = await thisModule(); 161 | result.vw.should.equal(customVW); 162 | })); 163 | 164 | it("should return the custom vh", (async () => { 165 | let result = await thisModule(); 166 | result.vh.should.equal(customVH); 167 | })); 168 | 169 | it("should return the custom threshold", (async () => { 170 | let result = await thisModule(); 171 | result.threshold.should.equal(customThreshold); 172 | })); 173 | 174 | it("should return the custom wait-value", (async () => { 175 | let result = await thisModule(); 176 | result.wait.should.equal(customWait); 177 | })); 178 | 179 | }); 180 | }); 181 | -------------------------------------------------------------------------------- /test/api/check-regression.spec.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Specs for the regression check module. 3 | * Handles the regresion test for the provided items 4 | * It ensures the arguments have the correct form. 5 | * It creates the data-objects. 6 | * It makes screenshots. 7 | * It ensrures that there are reference items. 8 | * It compares the screenshot (regression item) against the reference item. 9 | * It returns an array of results. 10 | * 11 | * The specs use stubs for all dependencies. 12 | * There are specs for the dependend modules. 13 | * 14 | * @copyright 2017 Martin Krause (http://martinkr.github.io) 15 | * @license MIT license: https://opensource.org/licenses/MIT 16 | * 17 | * @author Martin Krause 18 | */ 19 | 20 | /* eslint-env mocha */ 21 | 22 | const fs = require("fs-extra-plus"); 23 | const path = require("path"); 24 | const crypto = require("crypto"); 25 | 26 | const proxyquire = require("proxyquire").noCallThru(); 27 | 28 | const thisModulePath = "api/check-regression"; 29 | let thisModule = require("./../../app/" + thisModulePath); 30 | 31 | 32 | const port = 3000; 33 | const server = require("chigai-mock-server"); 34 | let testServer; 35 | 36 | // create stubs for spying on them 37 | let stubResultsToJson = { 38 | "write": (items) => new Promise((resolve, reject) => { 39 | resolve(items); 40 | }) 41 | }; 42 | let spyResultsToJson = sinon.spy(stubResultsToJson, "write"); 43 | // mock dependencies 44 | const stubAndReturn = ((value) => { 45 | thisModule = proxyquire("./../../app/" + thisModulePath, { 46 | "./../adapter/puppeteer": (item) => { 47 | return new Promise((resolve, reject) => { 48 | item.screenshot = value; 49 | resolve(item); 50 | }); 51 | }, 52 | "./../adapter/blink-diff": (item) => { 53 | return new Promise((resolve, reject) => { 54 | item.match = value; 55 | resolve(item); 56 | }); 57 | }, 58 | "./../utils/reference-item": (item) => { 59 | return new Promise((resolve, reject) => { 60 | item.fresh = value; 61 | resolve(item); 62 | }); 63 | }, 64 | "./../utils/results-to-json": { 65 | "write": spyResultsToJson 66 | }, 67 | }); 68 | }); 69 | 70 | // setup data 71 | const dataPathDefault = path.join("./test/", "data"); 72 | const dataPathCustom = path.join("./", "/test/_tmp/customfolder"); 73 | const hash = crypto.createHash("sha512").update(`http://localhost:${port}/static` + 500 + 500).digest("hex"); 74 | const regressionItem = path.join(dataPathDefault, hash + "regression.png"); 75 | const referenceItem = path.join(dataPathDefault, hash + "reference.png"); 76 | const differenceItem = path.join(dataPathDefault, hash + "difference.png"); 77 | 78 | /** creates a data-object as argument */ 79 | const createItem = () => { 80 | let item = { 81 | "viewport": {} 82 | }; 83 | item.uri = `http://localhost:${port}/static`; 84 | item.viewport.width = 500; 85 | item.viewport.height = 500; 86 | item.hash = hash; 87 | item.regression_item = regressionItem; 88 | item.reference_item = referenceItem; 89 | item.difference_item = differenceItem; 90 | item.options = {}; 91 | return item; 92 | }; 93 | 94 | 95 | describe(`the module ${thisModulePath}`, () => { 96 | 97 | afterEach((done) => { 98 | spyResultsToJson.resetHistory(); 99 | testServer.close(); 100 | done(); 101 | }); 102 | 103 | beforeEach(async () => { 104 | testServer = server.listen(port); 105 | }); 106 | 107 | after(async () => { 108 | await fs.emptyDir(dataPathDefault); 109 | await fs.remove(dataPathCustom); 110 | spyResultsToJson.restore(); 111 | }); 112 | 113 | describe("should handle errors", () => { 114 | 115 | it("should throw if there's no argument)", (async () => { 116 | try { 117 | await thisModule(null); 118 | } catch (error) { 119 | return error.should.be.an.instanceof(Error); 120 | } 121 | throw new Error("should throw"); 122 | })); 123 | 124 | it("should throw if the argument is not an array", (async () => { 125 | try { 126 | await thisModule({}); 127 | } catch (error) { 128 | return error.should.be.an.instanceof(Error); 129 | } 130 | throw new Error("should throw"); 131 | })); 132 | 133 | it("should throw if there's no array as argument", (async () => { 134 | try { 135 | await thisModule({}); 136 | } catch (error) { 137 | return error.should.be.an.instanceof(Error); 138 | } 139 | throw new Error("should throw"); 140 | })); 141 | 142 | it("should throw if there's no propery \"uri\" in the object", (async () => { 143 | try { 144 | await thisModule([{ 145 | "foo": "bar" 146 | }]); 147 | } catch (error) { 148 | return error.should.be.an.instanceof(Error); 149 | } 150 | throw new Error("should throw"); 151 | })); 152 | 153 | }); 154 | 155 | describe("should work as expected", () => { 156 | 157 | it("should ensure the screenshot directory is available", (async () => { 158 | let result; 159 | let item = createItem(); 160 | item.options.path = dataPathCustom; 161 | stubAndReturn(true); 162 | result = await thisModule([item]); 163 | result = await fs.pathExists(dataPathCustom); 164 | result.should.be.ok; 165 | })); 166 | 167 | it("should give one result for one item ", (async () => { 168 | let result; 169 | stubAndReturn(true); 170 | result = await thisModule([createItem()]); 171 | result.should.have.a.lengthOf(1); 172 | })); 173 | 174 | it("should give two result for two itemx ", (async () => { 175 | let result; 176 | stubAndReturn(true); 177 | result = await thisModule([createItem(), createItem()]); 178 | result.should.have.a.lengthOf(2); 179 | })); 180 | 181 | it("should return an array", (async () => { 182 | let result; 183 | stubAndReturn(true); 184 | result = await thisModule([createItem()]); 185 | result.should.be.an("array"); 186 | })); 187 | 188 | // screenshot 189 | it("should return an array of result objects indicating \"screenshot successfull\" ", (async () => { 190 | let result; 191 | stubAndReturn(true); 192 | result = await thisModule([createItem()]); 193 | (result.shift().screenshot).should.be.true 194 | })); 195 | 196 | it("should return an array of result objects indicating \"screenshot failed\" ", (async () => { 197 | let result; 198 | stubAndReturn(false); 199 | result = await thisModule([createItem()]); 200 | (result.shift().screenshot).should.be.false 201 | })); 202 | 203 | // reference 204 | it("should return an array of result objects indicating \"reference was freshly created\" ", (async () => { 205 | let result; 206 | stubAndReturn(true); 207 | result = await thisModule([createItem()]); 208 | (result.shift().fresh).should.be.true 209 | })); 210 | 211 | it("should return an array of result objects indicating \"reference was already present\" ", (async () => { 212 | let result; 213 | stubAndReturn(false); 214 | result = await thisModule([createItem()]); 215 | (result.shift().fresh).should.be.false 216 | })); 217 | 218 | // compare 219 | it("should return an array of result objects indicating \"comparison successfull\" ", (async () => { 220 | let result; 221 | stubAndReturn(true); 222 | result = await thisModule([createItem()]); 223 | (result.shift().match).should.be.true 224 | })); 225 | 226 | 227 | it("should return an array of result objects indicating \"comparison failed\" ", (async () => { 228 | let result; 229 | stubAndReturn(false); 230 | result = await thisModule([createItem()]); 231 | (result.shift().match).should.be.false 232 | })); 233 | 234 | // write to json 235 | it("should write the results to file by calling the \"utils/results-to-json\" module", (async () => { 236 | stubAndReturn(false); 237 | await thisModule([createItem()]); 238 | spyResultsToJson.should.have.been.calledOnce; 239 | })); 240 | 241 | 242 | }); 243 | 244 | }); 245 | -------------------------------------------------------------------------------- /test/utils/results-to-json.spec.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Specs for the results to jons module. 4 | * It takes an array of result items (which currently contains one result) 5 | * It ensures the results.json is present in the "output path" (where the screenshots are) 6 | * It reads the result.json which contains an array of result items 7 | * It replaces a prior item in the file (by hash) or adds the current item to the array. 8 | * It saves the file. 9 | * 10 | * @copyright 2018 Martin Krause (http://martinkr.github.io) 11 | * @license MIT license: https://opensource.org/licenses/MIT 12 | * 13 | * @author Martin Krause 14 | */ 15 | 16 | /* eslint-env mocha */ 17 | const fs = require("fs-extra-plus"); 18 | const path = require("path"); 19 | const crypto = require("crypto"); 20 | 21 | const thisModulePath = "utils/results-to-json"; 22 | const thisModule = require("./../../app/" + thisModulePath).write; 23 | 24 | // setup data 25 | const dataFile = "results.json"; 26 | const dataPathDefault = path.join(__dirname, "screenshots"); 27 | const pathFile = path.join(dataPathDefault,"/",dataFile); 28 | const hash = crypto.createHash("sha512").update(`http://localhost:3000/static` + 1024 + 786).digest("hex"); 29 | 30 | /** creates a data-object as argument */ 31 | const createItem = (suffix, counter) => { 32 | let item = { "viewport": {} }; 33 | item.hash = suffix ? hash + suffix : hash; 34 | item.path = dataPathDefault; 35 | item.count = counter ? counter : 0; 36 | return item; 37 | }; 38 | 39 | describe(`the module ${thisModulePath}`, () => { 40 | 41 | afterEach((done) => { 42 | done(); 43 | }); 44 | 45 | beforeEach(async () => { 46 | await fs.emptyDir(dataPathDefault); 47 | await fs.ensureFile(pathFile); 48 | }) 49 | 50 | after(async() => { 51 | }); 52 | 53 | describe("should take on argument, an \"array\"", () => { 54 | 55 | it("should throw if the first argument is an \"object\" instead of an \"array\"", (async () => { 56 | try { 57 | await thisModule({}); 58 | } catch (error) { 59 | return error.should.be.an.instanceof(Error); 60 | } 61 | throw new Error("should throw"); 62 | })); 63 | 64 | it("should throw if the first argument is a \"Number\" instead of an \"array\"", (async () => { 65 | try { 66 | await thisModule(1); 67 | } catch (error) { 68 | return error.should.be.an.instanceof(Error); 69 | } 70 | throw new Error("should throw"); 71 | })); 72 | 73 | it("should throw if the first argument is a \"String\" instead of an \"array\"", (async () => { 74 | try { 75 | await thisModule("string"); 76 | } catch (error) { 77 | return error.should.be.an.instanceof(Error); 78 | } 79 | throw new Error("should throw"); 80 | })); 81 | 82 | it("should throw if the first argument is an \"Object\" instead of an \"array\"", (async () => { 83 | try { 84 | await thisModule({}); 85 | } catch (error) { 86 | return error.should.be.an.instanceof(Error); 87 | } 88 | throw new Error("should throw"); 89 | })); 90 | 91 | it("should throw if the first argument is a Boolean \"true\" instead of an \"array\"", (async () => { 92 | try { 93 | await thisModule(true); 94 | } catch (error) { 95 | return error.should.be.an.instanceof(Error); 96 | } 97 | throw new Error("should throw"); 98 | })); 99 | 100 | it("should throw if the first argument is a Boolean \"false\" instead of an \"array\"", (async () => { 101 | try { 102 | await thisModule(false); 103 | } catch (error) { 104 | return error.should.be.an.instanceof(Error); 105 | } 106 | throw new Error("should throw"); 107 | })); 108 | 109 | it("should throw if the first argument is \"null\" instead of an \"array\"", (async () => { 110 | try { 111 | await thisModule(null); 112 | } catch (error) { 113 | return error.should.be.an.instanceof(Error); 114 | } 115 | throw new Error("should throw"); 116 | })); 117 | 118 | it("should throw if the first argument is \"undefined\" instead of an \"array\"", (async () => { 119 | try { 120 | await thisModule(undefined); 121 | } catch (error) { 122 | return error.should.be.an.instanceof(Error); 123 | } 124 | throw new Error("should throw"); 125 | })); 126 | 127 | it("should throw if the first argument is missing instead of an \"array\"", (async () => { 128 | try { 129 | await thisModule(); 130 | } catch (error) { 131 | return error.should.be.an.instanceof(Error); 132 | } 133 | throw new Error("should throw"); 134 | })); 135 | 136 | it("should take an \"array\" as an argument", (async () => { 137 | let result; 138 | result = await thisModule([]); 139 | result.should.be.ok; 140 | })); 141 | 142 | it("should take an empty \"array\" as an argument and return true", (async () => { 143 | let result; 144 | result = await thisModule([]); 145 | result.should.be.ok; 146 | })); 147 | 148 | it("should take an \"array\" with one item as an argument and return true", (async () => { 149 | let result; 150 | result = await thisModule([createItem()]); 151 | result.should.be.ok; 152 | })); 153 | 154 | }); 155 | 156 | describe("should work as expected", () => { 157 | 158 | 159 | 160 | it("should ensure the data.json is available", (async() => { 161 | let result; 162 | await fs.remove(pathFile); 163 | await thisModule([createItem()]); 164 | result = await fs.pathExists(pathFile); 165 | result.should.be.ok; 166 | })); 167 | 168 | 169 | 170 | it("should do nothing and return true if there's nothing in the file and no item", (async() => { 171 | let result; 172 | await fs.remove(pathFile); 173 | result = await thisModule([]); 174 | 175 | result.should.be.true; 176 | })); 177 | 178 | it("should do nothing and return true if there's an empty array in the file and no item", (async() => { 179 | let result; 180 | await fs.remove(pathFile); 181 | await fs.outputJson(pathFile, []); 182 | 183 | result = await thisModule([]); 184 | result.should.be.true; 185 | 186 | result = await fs.readJSON(pathFile); 187 | result.should.be.deep.equal([]); 188 | })); 189 | 190 | it("should write an array with the item if there's an empty array in the file and one item", (async() => { 191 | let result; 192 | let item = createItem(); 193 | await fs.remove(pathFile); 194 | await fs.outputJson(pathFile, []); 195 | 196 | await thisModule([item]); 197 | 198 | result = await fs.readJSON(pathFile); 199 | result.should.be.deep.equal([item]); 200 | })); 201 | 202 | it("should write an array with two items if there's one item in the array of the file and one item", (async() => { 203 | let result; 204 | let item1 = createItem(); 205 | let item2 = createItem("_1"); 206 | await fs.remove(pathFile); 207 | await fs.outputJson(pathFile, [item1]); 208 | 209 | 210 | await thisModule([item2]); 211 | 212 | result = await fs.readJSON(pathFile); 213 | result.should.be.deep.equal([item1,item2]); 214 | })); 215 | 216 | it("should write an array with one updated item if there's one item in the array which is the same item at the begining", (async() => { 217 | let result; 218 | let item1 = createItem(); 219 | let item1_1 = createItem(null, 1); 220 | let item2 = createItem("_1"); 221 | let item3 = createItem("_2"); 222 | let item4 = createItem("_3"); 223 | await fs.remove(pathFile); 224 | await fs.outputJson(pathFile, [item1,item2,item3,item4]); 225 | 226 | await thisModule([item1_1]); 227 | 228 | result = await fs.readJSON(pathFile); 229 | result.should.be.deep.equal([item1_1,item2,item3,item4]); 230 | })); 231 | 232 | it("should write an array with one updated item if there's one item in the array which is the same item at the end ", (async() => { 233 | let result; 234 | let item1 = createItem(); 235 | let item2 = createItem("_1"); 236 | let item3 = createItem("_2"); 237 | let item4 = createItem("_3"); 238 | let item4_1 = createItem("_3", 1); 239 | await fs.remove(pathFile); 240 | await fs.outputJson(pathFile, [item1,item2,item3,item4]); 241 | 242 | await thisModule([item4_1]); 243 | 244 | result = await fs.readJSON(pathFile); 245 | result.should.be.deep.equal([item1,item2,item3,item4_1]); 246 | })); 247 | 248 | it("should write an array with one updated item if there's one item in the array which is the same item in the middle", (async() => { 249 | let result; 250 | let item1 = createItem(); 251 | let item2 = createItem("_1"); 252 | let item2_1 = createItem("_1", 1); 253 | let item3 = createItem("_2"); 254 | let item4 = createItem("_3"); 255 | await fs.remove(pathFile); 256 | await fs.outputJson(pathFile, [item1,item2,item3,item4]); 257 | 258 | await thisModule([item2_1]); 259 | 260 | result = await fs.readJSON(pathFile); 261 | result.should.be.deep.equal([item1,item2_1,item3,item4]); 262 | })); 263 | 264 | it("should work even if the existing file is not an array aka corrupt", (async() => { 265 | let result; 266 | let item1 = createItem(); 267 | await fs.remove(pathFile); 268 | await fs.outputJson(pathFile, {"should":"be an array not an object"}); 269 | 270 | result = await thisModule([item1]); 271 | result.should.be.true; 272 | 273 | result = await fs.readJSON(pathFile); 274 | result.should.be.deep.equal([item1]); 275 | })); 276 | 277 | 278 | }); 279 | }); 280 | -------------------------------------------------------------------------------- /test/main.spec.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Specs for the main entry point 3 | * This module provides the external api of chigai-core 4 | * The specs are true end-to-end tests. 5 | * 6 | * @copyright 2016, 2017 Martin Krause (http://martinkr.github.io) 7 | * @license MIT license: https://opensource.org/licenses/MIT 8 | * 9 | * @author Martin Krause 10 | */ 11 | 12 | /* eslint-env mocha */ 13 | // const fs = require("fs-extra-plus"); 14 | const path = require("path"); 15 | const crypto = require("crypto"); 16 | const fs = require("fs-extra-plus"); 17 | 18 | const thisModulePath = "main"; 19 | const thisModule = require("./../app/" + thisModulePath); 20 | const imageSize = require("image-size"); 21 | 22 | const port = 3000; 23 | const server = require("chigai-mock-server"); 24 | let testServer; 25 | 26 | const uriDynamic = `http://localhost:${port}/random`; 27 | const uriStatic = `http://localhost:${port}/static`; 28 | 29 | 30 | describe(`the module ${thisModulePath}`, () => { 31 | 32 | afterEach((done) => { 33 | testServer.close(); 34 | done(); 35 | }); 36 | 37 | beforeEach(async () => { 38 | testServer = server.listen(port); 39 | await fs.emptyDir(path.join("./", "screenshots")); 40 | }); 41 | 42 | after(async () => { 43 | await fs.emptyDir(path.join("./", "screenshots")); 44 | }); 45 | 46 | describe("should provide an API for regression testing. It:", () => { 47 | 48 | it("should export a function \"regression\"", (async () => { 49 | thisModule.regression.should.be.a("function"); 50 | })); 51 | 52 | it("should throw if theres no argument for uri", (async () => { 53 | try { 54 | await thisModule.regression(null); 55 | } catch (error) { 56 | return error.should.be.an.instanceof(Error); 57 | } 58 | throw new Error("should throw"); 59 | })); 60 | 61 | it("should throw if theres no argument for uri", (async () => { 62 | try { 63 | await thisModule.regression(null); 64 | } catch (error) { 65 | return error.should.be.an.instanceof(Error); 66 | } 67 | throw new Error("should throw"); 68 | })); 69 | 70 | it("should return an array", (async () => { 71 | let result; 72 | result = await thisModule.regression(uriStatic, { 73 | "vw": 500, 74 | "vh": 500 75 | }); 76 | result.should.be.an("array"); 77 | })); 78 | 79 | it("should return an array with one item", (async () => { 80 | let result; 81 | result = await thisModule.regression(uriStatic, { 82 | "vw": 500, 83 | "vh": 500 84 | }); 85 | result.should.have.a.lengthOf(1); 86 | })); 87 | 88 | it("should create a screenshot of the given url - the regression item", (async () => { 89 | let result; 90 | let resultItem; 91 | let exists; 92 | result = await thisModule.regression(uriStatic, { 93 | "vw": 500, 94 | "vh": 500 95 | }); 96 | resultItem = result.shift(); 97 | exists = await fs.pathExists((resultItem.regression_item)); 98 | resultItem.screenshot.should.be.ok; 99 | exists.should.be.ok; 100 | })); 101 | 102 | 103 | it("should create a screenshot of the given url - with the default \"width\" if no \"vw\" is given", (async () => { 104 | let result; 105 | let resultItem; 106 | result = await thisModule.regression(uriStatic, { 107 | "vh": 500 108 | }); 109 | resultItem = result.shift(); 110 | result = imageSize(resultItem.regression_item); 111 | result.width.should.equal(1024); 112 | })); 113 | 114 | 115 | it("should create a screenshot of the given url - with the default \"height\" if no \"vh\" is given", (async () => { 116 | let result; 117 | let resultItem; 118 | result = await thisModule.regression(uriStatic, { 119 | "vw": 500 120 | }); 121 | resultItem = result.shift(); 122 | result = imageSize(resultItem.regression_item); 123 | result.height.should.equal(786); 124 | })); 125 | 126 | it("should create a screenshot of the given url - with the supplied \"width\" if a \"vw\"-option is given", (async () => { 127 | let result; 128 | let resultItem; 129 | result = await thisModule.regression(uriStatic, { 130 | "vw": 400 131 | }); 132 | resultItem = result.shift(); 133 | result = imageSize(resultItem.regression_item); 134 | result.width.should.equal(400); 135 | })); 136 | 137 | 138 | it("should create a screenshot of the given url - with the supplied \"height\" if a \"vh\"-option is given", (async () => { 139 | let result; 140 | let resultItem; 141 | result = await thisModule.regression(uriStatic, { 142 | "vh": 400 143 | }); 144 | resultItem = result.shift(); 145 | result = imageSize(resultItem.regression_item); 146 | result.height.should.equal(400); 147 | })); 148 | 149 | it("should create a screenshot of the given url - but wait a \"wait\"-option is given", (async () => { 150 | let time = Date.now(); 151 | let wait = 5000; 152 | 153 | await thisModule.regression(uriStatic, { 154 | "wait": wait 155 | }); 156 | 157 | (Date.now() - time).should.be.above(wait - 1); 158 | })); 159 | 160 | it("should create a screenshot of the given url - but use the supplied threshold if a \"threshold\"-option is given", (async () => { 161 | let result; 162 | let resultItem; 163 | 164 | // first 165 | await thisModule.regression(uriDynamic, {}); 166 | // compare 167 | result = await thisModule.regression(uriDynamic, { 168 | "threshold": 50 169 | }); 170 | resultItem = result.shift(); 171 | resultItem.match.should.be.ok; 172 | // compare 173 | result = await thisModule.regression(uriDynamic, { 174 | "threshold": 0 175 | }); 176 | resultItem = result.shift(); 177 | resultItem.match.should.not.be.ok; 178 | })); 179 | 180 | it("should create the reference item if none exists", (async () => { 181 | let result; 182 | let resultItem; 183 | let exists; 184 | result = await thisModule.regression(uriStatic, { 185 | "vw": 500, 186 | "vh": 500 187 | }); 188 | resultItem = result.shift(); 189 | exists = await fs.pathExists((resultItem.reference_item)); 190 | resultItem.screenshot.should.be.ok; 191 | resultItem.fresh.should.be.ok; 192 | exists.should.be.ok; 193 | })); 194 | 195 | it("should create the difference image", (async () => { 196 | let result; 197 | let resultItem; 198 | let exists; 199 | result = await thisModule.regression(uriStatic, { 200 | "vw": 500, 201 | "vh": 500 202 | }); 203 | resultItem = result.shift(); 204 | exists = await fs.pathExists((resultItem.difference_item)); 205 | resultItem.screenshot.should.be.ok; 206 | exists.should.be.ok; 207 | })); 208 | 209 | it("should set \"match\" to true if the images are considered being the same", (async () => { 210 | let result; 211 | let resultItem; 212 | // first 213 | result = await thisModule.regression(uriStatic, { 214 | "vw": 500, 215 | "vh": 500 216 | }); 217 | // compare 218 | result = await thisModule.regression(uriStatic, { 219 | "vw": 500, 220 | "vh": 500 221 | }); 222 | resultItem = result.shift(); 223 | resultItem.match.should.be.ok; 224 | })); 225 | 226 | it("should set \"match\" to false if the images are considered being different", (async () => { 227 | let result; 228 | let resultItem; 229 | // first 230 | await thisModule.regression(uriDynamic, { 231 | "vw": 500, 232 | "vh": 500 233 | }); 234 | // compare 235 | result = await thisModule.regression(uriDynamic, { 236 | "vw": 500, 237 | "vh": 500, 238 | "threshold": 0.000001 239 | }); 240 | resultItem = result.shift(); 241 | resultItem.match.should.not.be.ok; 242 | })); 243 | 244 | it("should just display message on \"-d\" / \"dry run\" and return \"false\"", (async () => { 245 | let result; 246 | result = await thisModule.regression(uriDynamic, { 247 | "d": true 248 | }); 249 | result.should.not.be.ok; 250 | })); 251 | 252 | }); 253 | 254 | 255 | describe("should provide an API for setting a new reference. It:", () => { 256 | 257 | it("should export a function \"reference\"", (async () => { 258 | thisModule.reference.should.be.a("function"); 259 | })); 260 | 261 | it("should throw if theres no argument for uri", (async () => { 262 | try { 263 | await thisModule.reference(null); 264 | } catch (error) { 265 | return error.should.be.an.instanceof(Error); 266 | } 267 | throw new Error("should throw"); 268 | })); 269 | 270 | it("should throw if theres no argument for uri", (async () => { 271 | try { 272 | await thisModule.reference(null); 273 | } catch (error) { 274 | return error.should.be.an.instanceof(Error); 275 | } 276 | throw new Error("should throw"); 277 | })); 278 | 279 | it("should return an array", (async () => { 280 | let result; 281 | result = await thisModule.reference(uriStatic, { 282 | "vw": 500, 283 | "vh": 500 284 | }); 285 | result.should.be.an("array"); 286 | })); 287 | 288 | it("should return an array with one item", (async () => { 289 | let result; 290 | result = await thisModule.reference(uriStatic, { 291 | "vw": 500, 292 | "vh": 500 293 | }); 294 | result.should.have.a.lengthOf(1); 295 | })); 296 | 297 | it("should create a reference item if none exists", (async () => { 298 | let result; 299 | let resultItem; 300 | let exists; 301 | result = await thisModule.reference(uriStatic, { 302 | "vw": 500, 303 | "vh": 500 304 | }); 305 | resultItem = result.shift(); 306 | exists = await fs.pathExists((resultItem.reference_item)); 307 | resultItem.fresh.should.be.ok; 308 | exists.should.be.ok; 309 | })); 310 | 311 | it("should create the reference item if there's already an existing one", (async () => { 312 | let result; 313 | let resultItem; 314 | let exists; 315 | result = await thisModule.reference(uriStatic, { 316 | "vw": 500, 317 | "vh": 500 318 | }); 319 | resultItem = result.shift(); 320 | exists = await fs.pathExists((resultItem.reference_item)); 321 | resultItem.fresh.should.be.ok; 322 | exists.should.be.ok; 323 | })); 324 | 325 | it("should just display message on \"-d\" / \"dry run\" and return \"false\"", (async () => { 326 | let result; 327 | result = await thisModule.reference(uriDynamic, { 328 | "d": true 329 | }); 330 | result.should.not.be.ok; 331 | })); 332 | 333 | 334 | 335 | }); 336 | 337 | }); 338 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@sinonjs/formatio@^2.0.0": 6 | version "2.0.0" 7 | resolved "https://registry.yarnpkg.com/@sinonjs/formatio/-/formatio-2.0.0.tgz#84db7e9eb5531df18a8c5e0bfb6e449e55e654b2" 8 | dependencies: 9 | samsam "1.3.0" 10 | 11 | "@types/fs-extra@^4.0.1": 12 | version "4.0.2" 13 | resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-4.0.2.tgz#7b9b1bbf85962cbe029b5a83c9b530d7c75af3ba" 14 | dependencies: 15 | "@types/node" "*" 16 | 17 | "@types/node@*": 18 | version "8.0.30" 19 | resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.30.tgz#aa3c42946fc6357737eb215349fe728b38679d05" 20 | 21 | accepts@^1.2.2: 22 | version "1.3.5" 23 | resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.5.tgz#eb777df6011723a3b14e8a72c0805c8e86746bd2" 24 | dependencies: 25 | mime-types "~2.1.18" 26 | negotiator "0.6.1" 27 | 28 | acorn-jsx@^3.0.0: 29 | version "3.0.1" 30 | resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b" 31 | dependencies: 32 | acorn "^3.0.4" 33 | 34 | acorn@^3.0.4: 35 | version "3.3.0" 36 | resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" 37 | 38 | acorn@^5.1.1: 39 | version "5.1.2" 40 | resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.1.2.tgz#911cb53e036807cf0fa778dc5d370fbd864246d7" 41 | 42 | agent-base@^4.1.0: 43 | version "4.1.1" 44 | resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.1.1.tgz#92d8a4fc2524a3b09b3666a33b6c97960f23d6a4" 45 | dependencies: 46 | es6-promisify "^5.0.0" 47 | 48 | ajv-keywords@^1.0.0: 49 | version "1.5.1" 50 | resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b886daf3c" 51 | 52 | ajv@^4.7.0: 53 | version "4.11.8" 54 | resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536" 55 | dependencies: 56 | co "^4.6.0" 57 | json-stable-stringify "^1.0.1" 58 | 59 | ajv@^5.1.0, ajv@^5.2.0: 60 | version "5.2.2" 61 | resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.2.2.tgz#47c68d69e86f5d953103b0074a9430dc63da5e39" 62 | dependencies: 63 | co "^4.6.0" 64 | fast-deep-equal "^1.0.0" 65 | json-schema-traverse "^0.3.0" 66 | json-stable-stringify "^1.0.1" 67 | 68 | align-text@^0.1.1, align-text@^0.1.3: 69 | version "0.1.4" 70 | resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" 71 | dependencies: 72 | kind-of "^3.0.2" 73 | longest "^1.0.1" 74 | repeat-string "^1.5.2" 75 | 76 | amdefine@>=0.0.4: 77 | version "1.0.1" 78 | resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" 79 | 80 | ansi-escapes@^3.0.0: 81 | version "3.0.0" 82 | resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.0.0.tgz#ec3e8b4e9f8064fc02c3ac9b65f1c275bda8ef92" 83 | 84 | ansi-regex@^2.0.0: 85 | version "2.1.1" 86 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" 87 | 88 | ansi-regex@^3.0.0: 89 | version "3.0.0" 90 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" 91 | 92 | ansi-styles@^2.2.1: 93 | version "2.2.1" 94 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" 95 | 96 | ansi-styles@^3.1.0: 97 | version "3.2.0" 98 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.0.tgz#c159b8d5be0f9e5a6f346dab94f16ce022161b88" 99 | dependencies: 100 | color-convert "^1.9.0" 101 | 102 | any-promise@^1.1.0: 103 | version "1.3.0" 104 | resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" 105 | 106 | append-transform@^0.4.0: 107 | version "0.4.0" 108 | resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-0.4.0.tgz#d76ebf8ca94d276e247a36bad44a4b74ab611991" 109 | dependencies: 110 | default-require-extensions "^1.0.0" 111 | 112 | archy@^1.0.0: 113 | version "1.0.0" 114 | resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" 115 | 116 | argparse@^1.0.7: 117 | version "1.0.9" 118 | resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.9.tgz#73d83bc263f86e97f8cc4f6bae1b0e90a7d22c86" 119 | dependencies: 120 | sprintf-js "~1.0.2" 121 | 122 | arr-diff@^2.0.0: 123 | version "2.0.0" 124 | resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" 125 | dependencies: 126 | arr-flatten "^1.0.1" 127 | 128 | arr-diff@^4.0.0: 129 | version "4.0.0" 130 | resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" 131 | 132 | arr-flatten@^1.0.1, arr-flatten@^1.1.0: 133 | version "1.1.0" 134 | resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" 135 | 136 | arr-union@^3.1.0: 137 | version "3.1.0" 138 | resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" 139 | 140 | array-union@^1.0.1: 141 | version "1.0.2" 142 | resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" 143 | dependencies: 144 | array-uniq "^1.0.1" 145 | 146 | array-uniq@^1.0.1: 147 | version "1.0.3" 148 | resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" 149 | 150 | array-unique@^0.2.1: 151 | version "0.2.1" 152 | resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" 153 | 154 | array-unique@^0.3.2: 155 | version "0.3.2" 156 | resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" 157 | 158 | arrify@^1.0.0, arrify@^1.0.1: 159 | version "1.0.1" 160 | resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" 161 | 162 | asap@~1.0.0: 163 | version "1.0.0" 164 | resolved "https://registry.yarnpkg.com/asap/-/asap-1.0.0.tgz#b2a45da5fdfa20b0496fc3768cc27c12fa916a7d" 165 | 166 | asn1@~0.2.3: 167 | version "0.2.3" 168 | resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" 169 | 170 | assert-plus@1.0.0, assert-plus@^1.0.0: 171 | version "1.0.0" 172 | resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" 173 | 174 | assertion-error@^1.0.1: 175 | version "1.0.2" 176 | resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.0.2.tgz#13ca515d86206da0bac66e834dd397d87581094c" 177 | 178 | assign-symbols@^1.0.0: 179 | version "1.0.0" 180 | resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" 181 | 182 | async-limiter@~1.0.0: 183 | version "1.0.0" 184 | resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8" 185 | 186 | async@0.1.15: 187 | version "0.1.15" 188 | resolved "https://registry.yarnpkg.com/async/-/async-0.1.15.tgz#2180eaca2cf2a6ca5280d41c0585bec9b3e49bd3" 189 | 190 | async@^1.4.0: 191 | version "1.5.2" 192 | resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" 193 | 194 | asynckit@^0.4.0: 195 | version "0.4.0" 196 | resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" 197 | 198 | atob@^2.0.0: 199 | version "2.1.0" 200 | resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.0.tgz#ab2b150e51d7b122b9efc8d7340c06b6c41076bc" 201 | 202 | aws-sign2@~0.7.0: 203 | version "0.7.0" 204 | resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" 205 | 206 | aws4@^1.6.0: 207 | version "1.6.0" 208 | resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e" 209 | 210 | babel-code-frame@^6.22.0, babel-code-frame@^6.26.0: 211 | version "6.26.0" 212 | resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" 213 | dependencies: 214 | chalk "^1.1.3" 215 | esutils "^2.0.2" 216 | js-tokens "^3.0.2" 217 | 218 | babel-generator@^6.18.0: 219 | version "6.26.0" 220 | resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.26.0.tgz#ac1ae20070b79f6e3ca1d3269613053774f20dc5" 221 | dependencies: 222 | babel-messages "^6.23.0" 223 | babel-runtime "^6.26.0" 224 | babel-types "^6.26.0" 225 | detect-indent "^4.0.0" 226 | jsesc "^1.3.0" 227 | lodash "^4.17.4" 228 | source-map "^0.5.6" 229 | trim-right "^1.0.1" 230 | 231 | babel-messages@^6.23.0: 232 | version "6.23.0" 233 | resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e" 234 | dependencies: 235 | babel-runtime "^6.22.0" 236 | 237 | babel-runtime@^6.22.0, babel-runtime@^6.26.0: 238 | version "6.26.0" 239 | resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" 240 | dependencies: 241 | core-js "^2.4.0" 242 | regenerator-runtime "^0.11.0" 243 | 244 | babel-template@^6.16.0: 245 | version "6.26.0" 246 | resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.26.0.tgz#de03e2d16396b069f46dd9fff8521fb1a0e35e02" 247 | dependencies: 248 | babel-runtime "^6.26.0" 249 | babel-traverse "^6.26.0" 250 | babel-types "^6.26.0" 251 | babylon "^6.18.0" 252 | lodash "^4.17.4" 253 | 254 | babel-traverse@^6.18.0, babel-traverse@^6.26.0: 255 | version "6.26.0" 256 | resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee" 257 | dependencies: 258 | babel-code-frame "^6.26.0" 259 | babel-messages "^6.23.0" 260 | babel-runtime "^6.26.0" 261 | babel-types "^6.26.0" 262 | babylon "^6.18.0" 263 | debug "^2.6.8" 264 | globals "^9.18.0" 265 | invariant "^2.2.2" 266 | lodash "^4.17.4" 267 | 268 | babel-types@^6.18.0, babel-types@^6.26.0: 269 | version "6.26.0" 270 | resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497" 271 | dependencies: 272 | babel-runtime "^6.26.0" 273 | esutils "^2.0.2" 274 | lodash "^4.17.4" 275 | to-fast-properties "^1.0.3" 276 | 277 | babylon@^6.18.0: 278 | version "6.18.0" 279 | resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" 280 | 281 | balanced-match@^1.0.0: 282 | version "1.0.0" 283 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" 284 | 285 | base@^0.11.1: 286 | version "0.11.2" 287 | resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" 288 | dependencies: 289 | cache-base "^1.0.1" 290 | class-utils "^0.3.5" 291 | component-emitter "^1.2.1" 292 | define-property "^1.0.0" 293 | isobject "^3.0.1" 294 | mixin-deep "^1.2.0" 295 | pascalcase "^0.1.1" 296 | 297 | bcrypt-pbkdf@^1.0.0: 298 | version "1.0.1" 299 | resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d" 300 | dependencies: 301 | tweetnacl "^0.14.3" 302 | 303 | blink-diff@^1.0.13: 304 | version "1.0.13" 305 | resolved "https://registry.yarnpkg.com/blink-diff/-/blink-diff-1.0.13.tgz#80e3df69de804b30d40c70f041e983841ecda899" 306 | dependencies: 307 | pngjs-image "~0.11.5" 308 | preceptor-core "~0.10.0" 309 | promise "6.0.0" 310 | 311 | boom@4.x.x: 312 | version "4.3.1" 313 | resolved "https://registry.yarnpkg.com/boom/-/boom-4.3.1.tgz#4f8a3005cb4a7e3889f749030fd25b96e01d2e31" 314 | dependencies: 315 | hoek "4.x.x" 316 | 317 | boom@5.x.x: 318 | version "5.2.0" 319 | resolved "https://registry.yarnpkg.com/boom/-/boom-5.2.0.tgz#5dd9da6ee3a5f302077436290cb717d3f4a54e02" 320 | dependencies: 321 | hoek "4.x.x" 322 | 323 | brace-expansion@^1.1.7: 324 | version "1.1.8" 325 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292" 326 | dependencies: 327 | balanced-match "^1.0.0" 328 | concat-map "0.0.1" 329 | 330 | braces@^1.8.2: 331 | version "1.8.5" 332 | resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" 333 | dependencies: 334 | expand-range "^1.8.1" 335 | preserve "^0.2.0" 336 | repeat-element "^1.1.2" 337 | 338 | braces@^2.3.1: 339 | version "2.3.2" 340 | resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" 341 | dependencies: 342 | arr-flatten "^1.1.0" 343 | array-unique "^0.3.2" 344 | extend-shallow "^2.0.1" 345 | fill-range "^4.0.0" 346 | isobject "^3.0.1" 347 | repeat-element "^1.1.2" 348 | snapdragon "^0.8.1" 349 | snapdragon-node "^2.0.1" 350 | split-string "^3.0.2" 351 | to-regex "^3.0.1" 352 | 353 | browser-stdout@1.3.0: 354 | version "1.3.0" 355 | resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.0.tgz#f351d32969d32fa5d7a5567154263d928ae3bd1f" 356 | 357 | builtin-modules@^1.0.0: 358 | version "1.1.1" 359 | resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" 360 | 361 | cache-base@^1.0.1: 362 | version "1.0.1" 363 | resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" 364 | dependencies: 365 | collection-visit "^1.0.0" 366 | component-emitter "^1.2.1" 367 | get-value "^2.0.6" 368 | has-value "^1.0.0" 369 | isobject "^3.0.1" 370 | set-value "^2.0.0" 371 | to-object-path "^0.3.0" 372 | union-value "^1.0.0" 373 | unset-value "^1.0.0" 374 | 375 | caching-transform@^1.0.0: 376 | version "1.0.1" 377 | resolved "https://registry.yarnpkg.com/caching-transform/-/caching-transform-1.0.1.tgz#6dbdb2f20f8d8fbce79f3e94e9d1742dcdf5c0a1" 378 | dependencies: 379 | md5-hex "^1.2.0" 380 | mkdirp "^0.5.1" 381 | write-file-atomic "^1.1.4" 382 | 383 | caller-path@^0.1.0: 384 | version "0.1.0" 385 | resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f" 386 | dependencies: 387 | callsites "^0.2.0" 388 | 389 | callsites@^0.2.0: 390 | version "0.2.0" 391 | resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca" 392 | 393 | camelcase@^1.0.2: 394 | version "1.2.1" 395 | resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" 396 | 397 | camelcase@^4.1.0: 398 | version "4.1.0" 399 | resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" 400 | 401 | caseless@~0.12.0: 402 | version "0.12.0" 403 | resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" 404 | 405 | center-align@^0.1.1: 406 | version "0.1.3" 407 | resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad" 408 | dependencies: 409 | align-text "^0.1.3" 410 | lazy-cache "^1.0.3" 411 | 412 | chai@4.1.1: 413 | version "4.1.1" 414 | resolved "https://registry.yarnpkg.com/chai/-/chai-4.1.1.tgz#66e21279e6f3c6415ff8231878227900e2171b39" 415 | dependencies: 416 | assertion-error "^1.0.1" 417 | check-error "^1.0.1" 418 | deep-eql "^2.0.1" 419 | get-func-name "^2.0.0" 420 | pathval "^1.0.0" 421 | type-detect "^4.0.0" 422 | 423 | chalk@^1.1.1, chalk@^1.1.3: 424 | version "1.1.3" 425 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" 426 | dependencies: 427 | ansi-styles "^2.2.1" 428 | escape-string-regexp "^1.0.2" 429 | has-ansi "^2.0.0" 430 | strip-ansi "^3.0.0" 431 | supports-color "^2.0.0" 432 | 433 | chalk@^2.0.0: 434 | version "2.1.0" 435 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.1.0.tgz#ac5becf14fa21b99c6c92ca7a7d7cfd5b17e743e" 436 | dependencies: 437 | ansi-styles "^3.1.0" 438 | escape-string-regexp "^1.0.5" 439 | supports-color "^4.0.0" 440 | 441 | check-error@^1.0.1: 442 | version "1.0.2" 443 | resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" 444 | 445 | chigai-mock-server@^1.0.0: 446 | version "1.0.2" 447 | resolved "https://registry.yarnpkg.com/chigai-mock-server/-/chigai-mock-server-1.0.2.tgz#a78d8ff67ece66b63d917c4bd46db9cd1e067a0b" 448 | dependencies: 449 | koa "^2.3.0" 450 | koa-route "^3.2.0" 451 | 452 | circular-json@^0.3.1: 453 | version "0.3.3" 454 | resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66" 455 | 456 | class-utils@^0.3.5: 457 | version "0.3.6" 458 | resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" 459 | dependencies: 460 | arr-union "^3.1.0" 461 | define-property "^0.2.5" 462 | isobject "^3.0.0" 463 | static-extend "^0.1.1" 464 | 465 | cli-cursor@^2.1.0: 466 | version "2.1.0" 467 | resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" 468 | dependencies: 469 | restore-cursor "^2.0.0" 470 | 471 | cli-width@^2.0.0: 472 | version "2.2.0" 473 | resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" 474 | 475 | cliui@^2.1.0: 476 | version "2.1.0" 477 | resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1" 478 | dependencies: 479 | center-align "^0.1.1" 480 | right-align "^0.1.1" 481 | wordwrap "0.0.2" 482 | 483 | cliui@^4.0.0: 484 | version "4.0.0" 485 | resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.0.0.tgz#743d4650e05f36d1ed2575b59638d87322bfbbcc" 486 | dependencies: 487 | string-width "^2.1.1" 488 | strip-ansi "^4.0.0" 489 | wrap-ansi "^2.0.0" 490 | 491 | co@^4.6.0: 492 | version "4.6.0" 493 | resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" 494 | 495 | code-point-at@^1.0.0: 496 | version "1.1.0" 497 | resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" 498 | 499 | collection-visit@^1.0.0: 500 | version "1.0.0" 501 | resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" 502 | dependencies: 503 | map-visit "^1.0.0" 504 | object-visit "^1.0.0" 505 | 506 | color-convert@^1.9.0: 507 | version "1.9.0" 508 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.0.tgz#1accf97dd739b983bf994d56fec8f95853641b7a" 509 | dependencies: 510 | color-name "^1.1.1" 511 | 512 | color-name@^1.1.1: 513 | version "1.1.3" 514 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" 515 | 516 | combined-stream@^1.0.5, combined-stream@~1.0.5: 517 | version "1.0.5" 518 | resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009" 519 | dependencies: 520 | delayed-stream "~1.0.0" 521 | 522 | commander@2.9.0: 523 | version "2.9.0" 524 | resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" 525 | dependencies: 526 | graceful-readlink ">= 1.0.0" 527 | 528 | commondir@^1.0.1: 529 | version "1.0.1" 530 | resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" 531 | 532 | component-emitter@^1.2.1: 533 | version "1.2.1" 534 | resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" 535 | 536 | concat-map@0.0.1: 537 | version "0.0.1" 538 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 539 | 540 | concat-stream@1.6.0, concat-stream@^1.6.0: 541 | version "1.6.0" 542 | resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7" 543 | dependencies: 544 | inherits "^2.0.3" 545 | readable-stream "^2.2.2" 546 | typedarray "^0.0.6" 547 | 548 | content-disposition@~0.5.0: 549 | version "0.5.2" 550 | resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" 551 | 552 | content-type@^1.0.0: 553 | version "1.0.4" 554 | resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" 555 | 556 | convert-source-map@^1.5.1: 557 | version "1.5.1" 558 | resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.1.tgz#b8278097b9bc229365de5c62cf5fcaed8b5599e5" 559 | 560 | cookies@~0.7.0: 561 | version "0.7.1" 562 | resolved "https://registry.yarnpkg.com/cookies/-/cookies-0.7.1.tgz#7c8a615f5481c61ab9f16c833731bcb8f663b99b" 563 | dependencies: 564 | depd "~1.1.1" 565 | keygrip "~1.0.2" 566 | 567 | copy-descriptor@^0.1.0: 568 | version "0.1.1" 569 | resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" 570 | 571 | core-js@^2.4.0: 572 | version "2.5.1" 573 | resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.1.tgz#ae6874dc66937789b80754ff5428df66819ca50b" 574 | 575 | core-util-is@1.0.2, core-util-is@~1.0.0: 576 | version "1.0.2" 577 | resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" 578 | 579 | cross-spawn@^4: 580 | version "4.0.2" 581 | resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-4.0.2.tgz#7b9247621c23adfdd3856004a823cbe397424d41" 582 | dependencies: 583 | lru-cache "^4.0.1" 584 | which "^1.2.9" 585 | 586 | cross-spawn@^5.0.1, cross-spawn@^5.1.0: 587 | version "5.1.0" 588 | resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" 589 | dependencies: 590 | lru-cache "^4.0.1" 591 | shebang-command "^1.2.0" 592 | which "^1.2.9" 593 | 594 | cryptiles@3.x.x: 595 | version "3.1.2" 596 | resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-3.1.2.tgz#a89fbb220f5ce25ec56e8c4aa8a4fd7b5b0d29fe" 597 | dependencies: 598 | boom "5.x.x" 599 | 600 | dashdash@^1.12.0: 601 | version "1.14.1" 602 | resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" 603 | dependencies: 604 | assert-plus "^1.0.0" 605 | 606 | debug-log@^1.0.1: 607 | version "1.0.1" 608 | resolved "https://registry.yarnpkg.com/debug-log/-/debug-log-1.0.1.tgz#2307632d4c04382b8df8a32f70b895046d52745f" 609 | 610 | debug@*, debug@^3.1.0: 611 | version "3.1.0" 612 | resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" 613 | dependencies: 614 | ms "2.0.0" 615 | 616 | debug@2.2.0: 617 | version "2.2.0" 618 | resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da" 619 | dependencies: 620 | ms "0.7.1" 621 | 622 | debug@2.6.8: 623 | version "2.6.8" 624 | resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc" 625 | dependencies: 626 | ms "2.0.0" 627 | 628 | debug@^2.2.0, debug@^2.3.3, debug@^2.4.1, debug@^2.6.8: 629 | version "2.6.9" 630 | resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" 631 | dependencies: 632 | ms "2.0.0" 633 | 634 | decamelize@^1.0.0, decamelize@^1.1.1: 635 | version "1.2.0" 636 | resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" 637 | 638 | decode-uri-component@^0.2.0: 639 | version "0.2.0" 640 | resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" 641 | 642 | deep-eql@^2.0.1: 643 | version "2.0.2" 644 | resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-2.0.2.tgz#b1bac06e56f0a76777686d50c9feb75c2ed7679a" 645 | dependencies: 646 | type-detect "^3.0.0" 647 | 648 | deep-equal@~1.0.1: 649 | version "1.0.1" 650 | resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" 651 | 652 | deep-is@~0.1.3: 653 | version "0.1.3" 654 | resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" 655 | 656 | default-require-extensions@^1.0.0: 657 | version "1.0.0" 658 | resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-1.0.0.tgz#f37ea15d3e13ffd9b437d33e1a75b5fb97874cb8" 659 | dependencies: 660 | strip-bom "^2.0.0" 661 | 662 | define-property@^0.2.5: 663 | version "0.2.5" 664 | resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" 665 | dependencies: 666 | is-descriptor "^0.1.0" 667 | 668 | define-property@^1.0.0: 669 | version "1.0.0" 670 | resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" 671 | dependencies: 672 | is-descriptor "^1.0.0" 673 | 674 | define-property@^2.0.2: 675 | version "2.0.2" 676 | resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" 677 | dependencies: 678 | is-descriptor "^1.0.2" 679 | isobject "^3.0.1" 680 | 681 | del@^2.0.2: 682 | version "2.2.2" 683 | resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8" 684 | dependencies: 685 | globby "^5.0.0" 686 | is-path-cwd "^1.0.0" 687 | is-path-in-cwd "^1.0.0" 688 | object-assign "^4.0.1" 689 | pify "^2.0.0" 690 | pinkie-promise "^2.0.0" 691 | rimraf "^2.2.8" 692 | 693 | delayed-stream@~1.0.0: 694 | version "1.0.0" 695 | resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" 696 | 697 | delegates@^1.0.0: 698 | version "1.0.0" 699 | resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" 700 | 701 | depd@1.1.1: 702 | version "1.1.1" 703 | resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.1.tgz#5783b4e1c459f06fa5ca27f991f3d06e7a310359" 704 | 705 | depd@^1.1.0, depd@~1.1.1: 706 | version "1.1.2" 707 | resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" 708 | 709 | destroy@^1.0.3: 710 | version "1.0.4" 711 | resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" 712 | 713 | detect-indent@^4.0.0: 714 | version "4.0.0" 715 | resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208" 716 | dependencies: 717 | repeating "^2.0.0" 718 | 719 | diff@3.2.0, diff@^3.1.0: 720 | version "3.2.0" 721 | resolved "https://registry.yarnpkg.com/diff/-/diff-3.2.0.tgz#c9ce393a4b7cbd0b058a725c93df299027868ff9" 722 | 723 | doctrine@^2.0.0: 724 | version "2.0.0" 725 | resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.0.0.tgz#c73d8d2909d22291e1a007a395804da8b665fe63" 726 | dependencies: 727 | esutils "^2.0.2" 728 | isarray "^1.0.0" 729 | 730 | ecc-jsbn@~0.1.1: 731 | version "0.1.1" 732 | resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505" 733 | dependencies: 734 | jsbn "~0.1.0" 735 | 736 | ee-first@1.1.1: 737 | version "1.1.1" 738 | resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" 739 | 740 | error-ex@^1.2.0: 741 | version "1.3.1" 742 | resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.1.tgz#f855a86ce61adc4e8621c3cda21e7a7612c3a8dc" 743 | dependencies: 744 | is-arrayish "^0.2.1" 745 | 746 | error-inject@~1.0.0: 747 | version "1.0.0" 748 | resolved "https://registry.yarnpkg.com/error-inject/-/error-inject-1.0.0.tgz#e2b3d91b54aed672f309d950d154850fa11d4f37" 749 | 750 | es6-promise@^4.0.3: 751 | version "4.1.1" 752 | resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.1.1.tgz#8811e90915d9a0dba36274f0b242dbda78f9c92a" 753 | 754 | es6-promisify@^5.0.0: 755 | version "5.0.0" 756 | resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" 757 | dependencies: 758 | es6-promise "^4.0.3" 759 | 760 | escape-html@~1.0.1: 761 | version "1.0.3" 762 | resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" 763 | 764 | escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: 765 | version "1.0.5" 766 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" 767 | 768 | eslint-scope@^3.7.1: 769 | version "3.7.1" 770 | resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.1.tgz#3d63c3edfda02e06e01a452ad88caacc7cdcb6e8" 771 | dependencies: 772 | esrecurse "^4.1.0" 773 | estraverse "^4.1.1" 774 | 775 | eslint@4.4.1: 776 | version "4.4.1" 777 | resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.4.1.tgz#99cd7eafcffca2ff99a5c8f5f2a474d6364b4bd3" 778 | dependencies: 779 | ajv "^5.2.0" 780 | babel-code-frame "^6.22.0" 781 | chalk "^1.1.3" 782 | concat-stream "^1.6.0" 783 | cross-spawn "^5.1.0" 784 | debug "^2.6.8" 785 | doctrine "^2.0.0" 786 | eslint-scope "^3.7.1" 787 | espree "^3.5.0" 788 | esquery "^1.0.0" 789 | estraverse "^4.2.0" 790 | esutils "^2.0.2" 791 | file-entry-cache "^2.0.0" 792 | functional-red-black-tree "^1.0.1" 793 | glob "^7.1.2" 794 | globals "^9.17.0" 795 | ignore "^3.3.3" 796 | imurmurhash "^0.1.4" 797 | inquirer "^3.0.6" 798 | is-resolvable "^1.0.0" 799 | js-yaml "^3.9.1" 800 | json-stable-stringify "^1.0.1" 801 | levn "^0.3.0" 802 | lodash "^4.17.4" 803 | minimatch "^3.0.2" 804 | mkdirp "^0.5.1" 805 | natural-compare "^1.4.0" 806 | optionator "^0.8.2" 807 | path-is-inside "^1.0.2" 808 | pluralize "^4.0.0" 809 | progress "^2.0.0" 810 | require-uncached "^1.0.3" 811 | semver "^5.3.0" 812 | strip-json-comments "~2.0.1" 813 | table "^4.0.1" 814 | text-table "~0.2.0" 815 | 816 | espree@^3.5.0: 817 | version "3.5.1" 818 | resolved "https://registry.yarnpkg.com/espree/-/espree-3.5.1.tgz#0c988b8ab46db53100a1954ae4ba995ddd27d87e" 819 | dependencies: 820 | acorn "^5.1.1" 821 | acorn-jsx "^3.0.0" 822 | 823 | esprima@^4.0.0: 824 | version "4.0.0" 825 | resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.0.tgz#4499eddcd1110e0b218bacf2fa7f7f59f55ca804" 826 | 827 | esquery@^1.0.0: 828 | version "1.0.0" 829 | resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.0.tgz#cfba8b57d7fba93f17298a8a006a04cda13d80fa" 830 | dependencies: 831 | estraverse "^4.0.0" 832 | 833 | esrecurse@^4.1.0: 834 | version "4.2.0" 835 | resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.0.tgz#fa9568d98d3823f9a41d91e902dcab9ea6e5b163" 836 | dependencies: 837 | estraverse "^4.1.0" 838 | object-assign "^4.0.1" 839 | 840 | estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: 841 | version "4.2.0" 842 | resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" 843 | 844 | esutils@^2.0.2: 845 | version "2.0.2" 846 | resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" 847 | 848 | execa@^0.7.0: 849 | version "0.7.0" 850 | resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" 851 | dependencies: 852 | cross-spawn "^5.0.1" 853 | get-stream "^3.0.0" 854 | is-stream "^1.1.0" 855 | npm-run-path "^2.0.0" 856 | p-finally "^1.0.0" 857 | signal-exit "^3.0.0" 858 | strip-eof "^1.0.0" 859 | 860 | expand-brackets@^0.1.4: 861 | version "0.1.5" 862 | resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" 863 | dependencies: 864 | is-posix-bracket "^0.1.0" 865 | 866 | expand-brackets@^2.1.4: 867 | version "2.1.4" 868 | resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" 869 | dependencies: 870 | debug "^2.3.3" 871 | define-property "^0.2.5" 872 | extend-shallow "^2.0.1" 873 | posix-character-classes "^0.1.0" 874 | regex-not "^1.0.0" 875 | snapdragon "^0.8.1" 876 | to-regex "^3.0.1" 877 | 878 | expand-range@^1.8.1: 879 | version "1.8.2" 880 | resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" 881 | dependencies: 882 | fill-range "^2.1.0" 883 | 884 | extend-shallow@^2.0.1: 885 | version "2.0.1" 886 | resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" 887 | dependencies: 888 | is-extendable "^0.1.0" 889 | 890 | extend-shallow@^3.0.0, extend-shallow@^3.0.2: 891 | version "3.0.2" 892 | resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" 893 | dependencies: 894 | assign-symbols "^1.0.0" 895 | is-extendable "^1.0.1" 896 | 897 | extend@~3.0.1: 898 | version "3.0.1" 899 | resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" 900 | 901 | external-editor@^2.0.4: 902 | version "2.0.5" 903 | resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.0.5.tgz#52c249a3981b9ba187c7cacf5beb50bf1d91a6bc" 904 | dependencies: 905 | iconv-lite "^0.4.17" 906 | jschardet "^1.4.2" 907 | tmp "^0.0.33" 908 | 909 | extglob@^0.3.1: 910 | version "0.3.2" 911 | resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" 912 | dependencies: 913 | is-extglob "^1.0.0" 914 | 915 | extglob@^2.0.4: 916 | version "2.0.4" 917 | resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" 918 | dependencies: 919 | array-unique "^0.3.2" 920 | define-property "^1.0.0" 921 | expand-brackets "^2.1.4" 922 | extend-shallow "^2.0.1" 923 | fragment-cache "^0.2.1" 924 | regex-not "^1.0.0" 925 | snapdragon "^0.8.1" 926 | to-regex "^3.0.1" 927 | 928 | extract-zip@^1.6.5: 929 | version "1.6.5" 930 | resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.6.5.tgz#99a06735b6ea20ea9b705d779acffcc87cff0440" 931 | dependencies: 932 | concat-stream "1.6.0" 933 | debug "2.2.0" 934 | mkdirp "0.5.0" 935 | yauzl "2.4.1" 936 | 937 | extsprintf@1.3.0, extsprintf@^1.2.0: 938 | version "1.3.0" 939 | resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" 940 | 941 | fast-deep-equal@^1.0.0: 942 | version "1.0.0" 943 | resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz#96256a3bc975595eb36d82e9929d060d893439ff" 944 | 945 | fast-levenshtein@~2.0.4: 946 | version "2.0.6" 947 | resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" 948 | 949 | fd-slicer@~1.0.1: 950 | version "1.0.1" 951 | resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.0.1.tgz#8b5bcbd9ec327c5041bf9ab023fd6750f1177e65" 952 | dependencies: 953 | pend "~1.2.0" 954 | 955 | figures@^2.0.0: 956 | version "2.0.0" 957 | resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" 958 | dependencies: 959 | escape-string-regexp "^1.0.5" 960 | 961 | file-entry-cache@^2.0.0: 962 | version "2.0.0" 963 | resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361" 964 | dependencies: 965 | flat-cache "^1.2.1" 966 | object-assign "^4.0.1" 967 | 968 | filename-regex@^2.0.0: 969 | version "2.0.1" 970 | resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" 971 | 972 | fill-keys@^1.0.2: 973 | version "1.0.2" 974 | resolved "https://registry.yarnpkg.com/fill-keys/-/fill-keys-1.0.2.tgz#9a8fa36f4e8ad634e3bf6b4f3c8882551452eb20" 975 | dependencies: 976 | is-object "~1.0.1" 977 | merge-descriptors "~1.0.0" 978 | 979 | fill-range@^2.1.0: 980 | version "2.2.3" 981 | resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.3.tgz#50b77dfd7e469bc7492470963699fe7a8485a723" 982 | dependencies: 983 | is-number "^2.1.0" 984 | isobject "^2.0.0" 985 | randomatic "^1.1.3" 986 | repeat-element "^1.1.2" 987 | repeat-string "^1.5.2" 988 | 989 | fill-range@^4.0.0: 990 | version "4.0.0" 991 | resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" 992 | dependencies: 993 | extend-shallow "^2.0.1" 994 | is-number "^3.0.0" 995 | repeat-string "^1.6.1" 996 | to-regex-range "^2.1.0" 997 | 998 | find-cache-dir@^0.1.1: 999 | version "0.1.1" 1000 | resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-0.1.1.tgz#c8defae57c8a52a8a784f9e31c57c742e993a0b9" 1001 | dependencies: 1002 | commondir "^1.0.1" 1003 | mkdirp "^0.5.1" 1004 | pkg-dir "^1.0.0" 1005 | 1006 | find-up@^1.0.0: 1007 | version "1.1.2" 1008 | resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" 1009 | dependencies: 1010 | path-exists "^2.0.0" 1011 | pinkie-promise "^2.0.0" 1012 | 1013 | find-up@^2.1.0: 1014 | version "2.1.0" 1015 | resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" 1016 | dependencies: 1017 | locate-path "^2.0.0" 1018 | 1019 | flat-cache@^1.2.1: 1020 | version "1.2.2" 1021 | resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.2.2.tgz#fa86714e72c21db88601761ecf2f555d1abc6b96" 1022 | dependencies: 1023 | circular-json "^0.3.1" 1024 | del "^2.0.2" 1025 | graceful-fs "^4.1.2" 1026 | write "^0.2.1" 1027 | 1028 | for-in@^1.0.1, for-in@^1.0.2: 1029 | version "1.0.2" 1030 | resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" 1031 | 1032 | for-own@^0.1.4: 1033 | version "0.1.5" 1034 | resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce" 1035 | dependencies: 1036 | for-in "^1.0.1" 1037 | 1038 | foreground-child@^1.5.3, foreground-child@^1.5.6: 1039 | version "1.5.6" 1040 | resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-1.5.6.tgz#4fd71ad2dfde96789b980a5c0a295937cb2f5ce9" 1041 | dependencies: 1042 | cross-spawn "^4" 1043 | signal-exit "^3.0.0" 1044 | 1045 | forever-agent@~0.6.1: 1046 | version "0.6.1" 1047 | resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" 1048 | 1049 | form-data@~2.3.1: 1050 | version "2.3.1" 1051 | resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.1.tgz#6fb94fbd71885306d73d15cc497fe4cc4ecd44bf" 1052 | dependencies: 1053 | asynckit "^0.4.0" 1054 | combined-stream "^1.0.5" 1055 | mime-types "^2.1.12" 1056 | 1057 | fragment-cache@^0.2.1: 1058 | version "0.2.1" 1059 | resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" 1060 | dependencies: 1061 | map-cache "^0.2.2" 1062 | 1063 | fresh@^0.5.2: 1064 | version "0.5.2" 1065 | resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" 1066 | 1067 | fs-extra-plus@^0.1.3: 1068 | version "0.1.3" 1069 | resolved "https://registry.yarnpkg.com/fs-extra-plus/-/fs-extra-plus-0.1.3.tgz#185fc616cbd9a36cf9451af34c1eade3c110e8ee" 1070 | dependencies: 1071 | "@types/fs-extra" "^4.0.1" 1072 | fs-extra "^4.0.1" 1073 | 1074 | fs-extra@^4.0.1: 1075 | version "4.0.2" 1076 | resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.2.tgz#f91704c53d1b461f893452b0c307d9997647ab6b" 1077 | dependencies: 1078 | graceful-fs "^4.1.2" 1079 | jsonfile "^4.0.0" 1080 | universalify "^0.1.0" 1081 | 1082 | fs.realpath@^1.0.0: 1083 | version "1.0.0" 1084 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 1085 | 1086 | functional-red-black-tree@^1.0.1: 1087 | version "1.0.1" 1088 | resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" 1089 | 1090 | get-caller-file@^1.0.1: 1091 | version "1.0.2" 1092 | resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.2.tgz#f702e63127e7e231c160a80c1554acb70d5047e5" 1093 | 1094 | get-func-name@^2.0.0: 1095 | version "2.0.0" 1096 | resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" 1097 | 1098 | get-stream@^3.0.0: 1099 | version "3.0.0" 1100 | resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" 1101 | 1102 | get-value@^2.0.3, get-value@^2.0.6: 1103 | version "2.0.6" 1104 | resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" 1105 | 1106 | getpass@^0.1.1: 1107 | version "0.1.7" 1108 | resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" 1109 | dependencies: 1110 | assert-plus "^1.0.0" 1111 | 1112 | glob-base@^0.3.0: 1113 | version "0.3.0" 1114 | resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" 1115 | dependencies: 1116 | glob-parent "^2.0.0" 1117 | is-glob "^2.0.0" 1118 | 1119 | glob-parent@^2.0.0: 1120 | version "2.0.0" 1121 | resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28" 1122 | dependencies: 1123 | is-glob "^2.0.0" 1124 | 1125 | glob@7.1.1: 1126 | version "7.1.1" 1127 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8" 1128 | dependencies: 1129 | fs.realpath "^1.0.0" 1130 | inflight "^1.0.4" 1131 | inherits "2" 1132 | minimatch "^3.0.2" 1133 | once "^1.3.0" 1134 | path-is-absolute "^1.0.0" 1135 | 1136 | glob@^7.0.3, glob@^7.0.5, glob@^7.0.6, glob@^7.1.2: 1137 | version "7.1.2" 1138 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" 1139 | dependencies: 1140 | fs.realpath "^1.0.0" 1141 | inflight "^1.0.4" 1142 | inherits "2" 1143 | minimatch "^3.0.4" 1144 | once "^1.3.0" 1145 | path-is-absolute "^1.0.0" 1146 | 1147 | globals@^9.17.0, globals@^9.18.0: 1148 | version "9.18.0" 1149 | resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" 1150 | 1151 | globby@^5.0.0: 1152 | version "5.0.0" 1153 | resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d" 1154 | dependencies: 1155 | array-union "^1.0.1" 1156 | arrify "^1.0.0" 1157 | glob "^7.0.3" 1158 | object-assign "^4.0.1" 1159 | pify "^2.0.0" 1160 | pinkie-promise "^2.0.0" 1161 | 1162 | graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6: 1163 | version "4.1.11" 1164 | resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" 1165 | 1166 | "graceful-readlink@>= 1.0.0": 1167 | version "1.0.1" 1168 | resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" 1169 | 1170 | growl@1.9.2: 1171 | version "1.9.2" 1172 | resolved "https://registry.yarnpkg.com/growl/-/growl-1.9.2.tgz#0ea7743715db8d8de2c5ede1775e1b45ac85c02f" 1173 | 1174 | handlebars@^4.0.3: 1175 | version "4.0.10" 1176 | resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.10.tgz#3d30c718b09a3d96f23ea4cc1f403c4d3ba9ff4f" 1177 | dependencies: 1178 | async "^1.4.0" 1179 | optimist "^0.6.1" 1180 | source-map "^0.4.4" 1181 | optionalDependencies: 1182 | uglify-js "^2.6" 1183 | 1184 | har-schema@^2.0.0: 1185 | version "2.0.0" 1186 | resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" 1187 | 1188 | har-validator@~5.0.3: 1189 | version "5.0.3" 1190 | resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.0.3.tgz#ba402c266194f15956ef15e0fcf242993f6a7dfd" 1191 | dependencies: 1192 | ajv "^5.1.0" 1193 | har-schema "^2.0.0" 1194 | 1195 | has-ansi@^2.0.0: 1196 | version "2.0.0" 1197 | resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" 1198 | dependencies: 1199 | ansi-regex "^2.0.0" 1200 | 1201 | has-flag@^1.0.0: 1202 | version "1.0.0" 1203 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" 1204 | 1205 | has-flag@^2.0.0: 1206 | version "2.0.0" 1207 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51" 1208 | 1209 | has-flag@^3.0.0: 1210 | version "3.0.0" 1211 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" 1212 | 1213 | has-value@^0.3.1: 1214 | version "0.3.1" 1215 | resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" 1216 | dependencies: 1217 | get-value "^2.0.3" 1218 | has-values "^0.1.4" 1219 | isobject "^2.0.0" 1220 | 1221 | has-value@^1.0.0: 1222 | version "1.0.0" 1223 | resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" 1224 | dependencies: 1225 | get-value "^2.0.6" 1226 | has-values "^1.0.0" 1227 | isobject "^3.0.0" 1228 | 1229 | has-values@^0.1.4: 1230 | version "0.1.4" 1231 | resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" 1232 | 1233 | has-values@^1.0.0: 1234 | version "1.0.0" 1235 | resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" 1236 | dependencies: 1237 | is-number "^3.0.0" 1238 | kind-of "^4.0.0" 1239 | 1240 | hawk@~6.0.2: 1241 | version "6.0.2" 1242 | resolved "https://registry.yarnpkg.com/hawk/-/hawk-6.0.2.tgz#af4d914eb065f9b5ce4d9d11c1cb2126eecc3038" 1243 | dependencies: 1244 | boom "4.x.x" 1245 | cryptiles "3.x.x" 1246 | hoek "4.x.x" 1247 | sntp "2.x.x" 1248 | 1249 | hoek@4.x.x: 1250 | version "4.2.0" 1251 | resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.0.tgz#72d9d0754f7fe25ca2d01ad8f8f9a9449a89526d" 1252 | 1253 | hosted-git-info@^2.1.4: 1254 | version "2.5.0" 1255 | resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.5.0.tgz#6d60e34b3abbc8313062c3b798ef8d901a07af3c" 1256 | 1257 | http-assert@^1.1.0: 1258 | version "1.3.0" 1259 | resolved "https://registry.yarnpkg.com/http-assert/-/http-assert-1.3.0.tgz#a31a5cf88c873ecbb5796907d4d6f132e8c01e4a" 1260 | dependencies: 1261 | deep-equal "~1.0.1" 1262 | http-errors "~1.6.1" 1263 | 1264 | http-errors@^1.2.8, http-errors@~1.6.1: 1265 | version "1.6.2" 1266 | resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.2.tgz#0a002cc85707192a7e7946ceedc11155f60ec736" 1267 | dependencies: 1268 | depd "1.1.1" 1269 | inherits "2.0.3" 1270 | setprototypeof "1.0.3" 1271 | statuses ">= 1.3.1 < 2" 1272 | 1273 | http-signature@~1.2.0: 1274 | version "1.2.0" 1275 | resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" 1276 | dependencies: 1277 | assert-plus "^1.0.0" 1278 | jsprim "^1.2.2" 1279 | sshpk "^1.7.0" 1280 | 1281 | https-proxy-agent@^2.1.0: 1282 | version "2.1.0" 1283 | resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.1.0.tgz#1391bee7fd66aeabc0df2a1fa90f58954f43e443" 1284 | dependencies: 1285 | agent-base "^4.1.0" 1286 | debug "^2.4.1" 1287 | 1288 | iconv-lite@^0.4.17, iconv-lite@^0.4.8: 1289 | version "0.4.19" 1290 | resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" 1291 | 1292 | ignore@^3.3.3: 1293 | version "3.3.5" 1294 | resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.5.tgz#c4e715455f6073a8d7e5dae72d2fc9d71663dba6" 1295 | 1296 | "image-size@https://github.com/image-size/image-size": 1297 | version "0.6.2" 1298 | resolved "https://github.com/image-size/image-size#1c30f8357cc432c456538e6f8c6ac02898f03510" 1299 | 1300 | imurmurhash@^0.1.4: 1301 | version "0.1.4" 1302 | resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" 1303 | 1304 | inflight@^1.0.4: 1305 | version "1.0.6" 1306 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 1307 | dependencies: 1308 | once "^1.3.0" 1309 | wrappy "1" 1310 | 1311 | inherits@2, inherits@2.0.3, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: 1312 | version "2.0.3" 1313 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" 1314 | 1315 | inquirer@^3.0.6: 1316 | version "3.3.0" 1317 | resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.3.0.tgz#9dd2f2ad765dcab1ff0443b491442a20ba227dc9" 1318 | dependencies: 1319 | ansi-escapes "^3.0.0" 1320 | chalk "^2.0.0" 1321 | cli-cursor "^2.1.0" 1322 | cli-width "^2.0.0" 1323 | external-editor "^2.0.4" 1324 | figures "^2.0.0" 1325 | lodash "^4.3.0" 1326 | mute-stream "0.0.7" 1327 | run-async "^2.2.0" 1328 | rx-lite "^4.0.8" 1329 | rx-lite-aggregates "^4.0.8" 1330 | string-width "^2.1.0" 1331 | strip-ansi "^4.0.0" 1332 | through "^2.3.6" 1333 | 1334 | invariant@^2.2.2: 1335 | version "2.2.2" 1336 | resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360" 1337 | dependencies: 1338 | loose-envify "^1.0.0" 1339 | 1340 | invert-kv@^1.0.0: 1341 | version "1.0.0" 1342 | resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" 1343 | 1344 | is-accessor-descriptor@^0.1.6: 1345 | version "0.1.6" 1346 | resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" 1347 | dependencies: 1348 | kind-of "^3.0.2" 1349 | 1350 | is-accessor-descriptor@^1.0.0: 1351 | version "1.0.0" 1352 | resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" 1353 | dependencies: 1354 | kind-of "^6.0.0" 1355 | 1356 | is-arrayish@^0.2.1: 1357 | version "0.2.1" 1358 | resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" 1359 | 1360 | is-buffer@^1.1.5: 1361 | version "1.1.5" 1362 | resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.5.tgz#1f3b26ef613b214b88cbca23cc6c01d87961eecc" 1363 | 1364 | is-builtin-module@^1.0.0: 1365 | version "1.0.0" 1366 | resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe" 1367 | dependencies: 1368 | builtin-modules "^1.0.0" 1369 | 1370 | is-data-descriptor@^0.1.4: 1371 | version "0.1.4" 1372 | resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" 1373 | dependencies: 1374 | kind-of "^3.0.2" 1375 | 1376 | is-data-descriptor@^1.0.0: 1377 | version "1.0.0" 1378 | resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" 1379 | dependencies: 1380 | kind-of "^6.0.0" 1381 | 1382 | is-descriptor@^0.1.0: 1383 | version "0.1.6" 1384 | resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" 1385 | dependencies: 1386 | is-accessor-descriptor "^0.1.6" 1387 | is-data-descriptor "^0.1.4" 1388 | kind-of "^5.0.0" 1389 | 1390 | is-descriptor@^1.0.0, is-descriptor@^1.0.2: 1391 | version "1.0.2" 1392 | resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" 1393 | dependencies: 1394 | is-accessor-descriptor "^1.0.0" 1395 | is-data-descriptor "^1.0.0" 1396 | kind-of "^6.0.2" 1397 | 1398 | is-dotfile@^1.0.0: 1399 | version "1.0.3" 1400 | resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" 1401 | 1402 | is-equal-shallow@^0.1.3: 1403 | version "0.1.3" 1404 | resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534" 1405 | dependencies: 1406 | is-primitive "^2.0.0" 1407 | 1408 | is-extendable@^0.1.0, is-extendable@^0.1.1: 1409 | version "0.1.1" 1410 | resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" 1411 | 1412 | is-extendable@^1.0.1: 1413 | version "1.0.1" 1414 | resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" 1415 | dependencies: 1416 | is-plain-object "^2.0.4" 1417 | 1418 | is-extglob@^1.0.0: 1419 | version "1.0.0" 1420 | resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" 1421 | 1422 | is-finite@^1.0.0: 1423 | version "1.0.2" 1424 | resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" 1425 | dependencies: 1426 | number-is-nan "^1.0.0" 1427 | 1428 | is-fullwidth-code-point@^1.0.0: 1429 | version "1.0.0" 1430 | resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" 1431 | dependencies: 1432 | number-is-nan "^1.0.0" 1433 | 1434 | is-fullwidth-code-point@^2.0.0: 1435 | version "2.0.0" 1436 | resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" 1437 | 1438 | is-generator-function@^1.0.3: 1439 | version "1.0.7" 1440 | resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.7.tgz#d2132e529bb0000a7f80794d4bdf5cd5e5813522" 1441 | 1442 | is-glob@^2.0.0, is-glob@^2.0.1: 1443 | version "2.0.1" 1444 | resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" 1445 | dependencies: 1446 | is-extglob "^1.0.0" 1447 | 1448 | is-number@^2.1.0: 1449 | version "2.1.0" 1450 | resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" 1451 | dependencies: 1452 | kind-of "^3.0.2" 1453 | 1454 | is-number@^3.0.0: 1455 | version "3.0.0" 1456 | resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" 1457 | dependencies: 1458 | kind-of "^3.0.2" 1459 | 1460 | is-number@^4.0.0: 1461 | version "4.0.0" 1462 | resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff" 1463 | 1464 | is-object@~1.0.1: 1465 | version "1.0.1" 1466 | resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.1.tgz#8952688c5ec2ffd6b03ecc85e769e02903083470" 1467 | 1468 | is-odd@^2.0.0: 1469 | version "2.0.0" 1470 | resolved "https://registry.yarnpkg.com/is-odd/-/is-odd-2.0.0.tgz#7646624671fd7ea558ccd9a2795182f2958f1b24" 1471 | dependencies: 1472 | is-number "^4.0.0" 1473 | 1474 | is-path-cwd@^1.0.0: 1475 | version "1.0.0" 1476 | resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d" 1477 | 1478 | is-path-in-cwd@^1.0.0: 1479 | version "1.0.0" 1480 | resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz#6477582b8214d602346094567003be8a9eac04dc" 1481 | dependencies: 1482 | is-path-inside "^1.0.0" 1483 | 1484 | is-path-inside@^1.0.0: 1485 | version "1.0.0" 1486 | resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.0.tgz#fc06e5a1683fbda13de667aff717bbc10a48f37f" 1487 | dependencies: 1488 | path-is-inside "^1.0.1" 1489 | 1490 | is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: 1491 | version "2.0.4" 1492 | resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" 1493 | dependencies: 1494 | isobject "^3.0.1" 1495 | 1496 | is-posix-bracket@^0.1.0: 1497 | version "0.1.1" 1498 | resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" 1499 | 1500 | is-primitive@^2.0.0: 1501 | version "2.0.0" 1502 | resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" 1503 | 1504 | is-promise@^2.1.0: 1505 | version "2.1.0" 1506 | resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" 1507 | 1508 | is-resolvable@^1.0.0: 1509 | version "1.0.0" 1510 | resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.0.0.tgz#8df57c61ea2e3c501408d100fb013cf8d6e0cc62" 1511 | dependencies: 1512 | tryit "^1.0.1" 1513 | 1514 | is-stream@^1.1.0: 1515 | version "1.1.0" 1516 | resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" 1517 | 1518 | is-typedarray@~1.0.0: 1519 | version "1.0.0" 1520 | resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" 1521 | 1522 | is-utf8@^0.2.0: 1523 | version "0.2.1" 1524 | resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" 1525 | 1526 | is-windows@^1.0.2: 1527 | version "1.0.2" 1528 | resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" 1529 | 1530 | isarray@0.0.1: 1531 | version "0.0.1" 1532 | resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" 1533 | 1534 | isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: 1535 | version "1.0.0" 1536 | resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" 1537 | 1538 | isexe@^2.0.0: 1539 | version "2.0.0" 1540 | resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" 1541 | 1542 | isobject@^2.0.0: 1543 | version "2.1.0" 1544 | resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" 1545 | dependencies: 1546 | isarray "1.0.0" 1547 | 1548 | isobject@^3.0.0, isobject@^3.0.1: 1549 | version "3.0.1" 1550 | resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" 1551 | 1552 | isstream@~0.1.2: 1553 | version "0.1.2" 1554 | resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" 1555 | 1556 | istanbul-lib-coverage@^1.1.2, istanbul-lib-coverage@^1.2.0: 1557 | version "1.2.0" 1558 | resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.0.tgz#f7d8f2e42b97e37fe796114cb0f9d68b5e3a4341" 1559 | 1560 | istanbul-lib-hook@^1.1.0: 1561 | version "1.1.0" 1562 | resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-1.1.0.tgz#8538d970372cb3716d53e55523dd54b557a8d89b" 1563 | dependencies: 1564 | append-transform "^0.4.0" 1565 | 1566 | istanbul-lib-instrument@^1.10.0: 1567 | version "1.10.1" 1568 | resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-1.10.1.tgz#724b4b6caceba8692d3f1f9d0727e279c401af7b" 1569 | dependencies: 1570 | babel-generator "^6.18.0" 1571 | babel-template "^6.16.0" 1572 | babel-traverse "^6.18.0" 1573 | babel-types "^6.18.0" 1574 | babylon "^6.18.0" 1575 | istanbul-lib-coverage "^1.2.0" 1576 | semver "^5.3.0" 1577 | 1578 | istanbul-lib-report@^1.1.3: 1579 | version "1.1.3" 1580 | resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-1.1.3.tgz#2df12188c0fa77990c0d2176d2d0ba3394188259" 1581 | dependencies: 1582 | istanbul-lib-coverage "^1.1.2" 1583 | mkdirp "^0.5.1" 1584 | path-parse "^1.0.5" 1585 | supports-color "^3.1.2" 1586 | 1587 | istanbul-lib-source-maps@^1.2.3: 1588 | version "1.2.3" 1589 | resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.3.tgz#20fb54b14e14b3fb6edb6aca3571fd2143db44e6" 1590 | dependencies: 1591 | debug "^3.1.0" 1592 | istanbul-lib-coverage "^1.1.2" 1593 | mkdirp "^0.5.1" 1594 | rimraf "^2.6.1" 1595 | source-map "^0.5.3" 1596 | 1597 | istanbul-reports@^1.1.4: 1598 | version "1.3.0" 1599 | resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-1.3.0.tgz#2f322e81e1d9520767597dca3c20a0cce89a3554" 1600 | dependencies: 1601 | handlebars "^4.0.3" 1602 | 1603 | js-tokens@^3.0.0, js-tokens@^3.0.2: 1604 | version "3.0.2" 1605 | resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" 1606 | 1607 | js-yaml@^3.9.1: 1608 | version "3.10.0" 1609 | resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.10.0.tgz#2e78441646bd4682e963f22b6e92823c309c62dc" 1610 | dependencies: 1611 | argparse "^1.0.7" 1612 | esprima "^4.0.0" 1613 | 1614 | jsbn@~0.1.0: 1615 | version "0.1.1" 1616 | resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" 1617 | 1618 | jschardet@^1.4.2: 1619 | version "1.5.1" 1620 | resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-1.5.1.tgz#c519f629f86b3a5bedba58a88d311309eec097f9" 1621 | 1622 | jsesc@^1.3.0: 1623 | version "1.3.0" 1624 | resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b" 1625 | 1626 | json-schema-traverse@^0.3.0: 1627 | version "0.3.1" 1628 | resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340" 1629 | 1630 | json-schema@0.2.3: 1631 | version "0.2.3" 1632 | resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" 1633 | 1634 | json-stable-stringify@^1.0.1: 1635 | version "1.0.1" 1636 | resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" 1637 | dependencies: 1638 | jsonify "~0.0.0" 1639 | 1640 | json-stringify-safe@~5.0.1: 1641 | version "5.0.1" 1642 | resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" 1643 | 1644 | json3@3.3.2: 1645 | version "3.3.2" 1646 | resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1" 1647 | 1648 | jsonfile@^4.0.0: 1649 | version "4.0.0" 1650 | resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" 1651 | optionalDependencies: 1652 | graceful-fs "^4.1.6" 1653 | 1654 | jsonify@~0.0.0: 1655 | version "0.0.0" 1656 | resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" 1657 | 1658 | jsprim@^1.2.2: 1659 | version "1.4.1" 1660 | resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" 1661 | dependencies: 1662 | assert-plus "1.0.0" 1663 | extsprintf "1.3.0" 1664 | json-schema "0.2.3" 1665 | verror "1.10.0" 1666 | 1667 | just-extend@^1.1.27: 1668 | version "1.1.27" 1669 | resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-1.1.27.tgz#ec6e79410ff914e472652abfa0e603c03d60e905" 1670 | 1671 | keygrip@~1.0.2: 1672 | version "1.0.2" 1673 | resolved "https://registry.yarnpkg.com/keygrip/-/keygrip-1.0.2.tgz#ad3297c557069dea8bcfe7a4fa491b75c5ddeb91" 1674 | 1675 | kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: 1676 | version "3.2.2" 1677 | resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" 1678 | dependencies: 1679 | is-buffer "^1.1.5" 1680 | 1681 | kind-of@^4.0.0: 1682 | version "4.0.0" 1683 | resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" 1684 | dependencies: 1685 | is-buffer "^1.1.5" 1686 | 1687 | kind-of@^5.0.0: 1688 | version "5.1.0" 1689 | resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" 1690 | 1691 | kind-of@^6.0.0, kind-of@^6.0.2: 1692 | version "6.0.2" 1693 | resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" 1694 | 1695 | koa-compose@^3.0.0: 1696 | version "3.2.1" 1697 | resolved "https://registry.yarnpkg.com/koa-compose/-/koa-compose-3.2.1.tgz#a85ccb40b7d986d8e5a345b3a1ace8eabcf54de7" 1698 | dependencies: 1699 | any-promise "^1.1.0" 1700 | 1701 | koa-compose@^4.0.0: 1702 | version "4.0.0" 1703 | resolved "https://registry.yarnpkg.com/koa-compose/-/koa-compose-4.0.0.tgz#2800a513d9c361ef0d63852b038e4f6f2d5a773c" 1704 | 1705 | koa-convert@^1.2.0: 1706 | version "1.2.0" 1707 | resolved "https://registry.yarnpkg.com/koa-convert/-/koa-convert-1.2.0.tgz#da40875df49de0539098d1700b50820cebcd21d0" 1708 | dependencies: 1709 | co "^4.6.0" 1710 | koa-compose "^3.0.0" 1711 | 1712 | koa-is-json@^1.0.0: 1713 | version "1.0.0" 1714 | resolved "https://registry.yarnpkg.com/koa-is-json/-/koa-is-json-1.0.0.tgz#273c07edcdcb8df6a2c1ab7d59ee76491451ec14" 1715 | 1716 | koa-route@^3.2.0: 1717 | version "3.2.0" 1718 | resolved "https://registry.yarnpkg.com/koa-route/-/koa-route-3.2.0.tgz#76298b99a6bcfa9e38cab6fe5c79a8733e758bce" 1719 | dependencies: 1720 | debug "*" 1721 | methods "~1.1.0" 1722 | path-to-regexp "^1.2.0" 1723 | 1724 | koa@^2.3.0: 1725 | version "2.5.0" 1726 | resolved "https://registry.yarnpkg.com/koa/-/koa-2.5.0.tgz#b0fbe1e195e43b27588a04fd0be0ddaeca2c154c" 1727 | dependencies: 1728 | accepts "^1.2.2" 1729 | content-disposition "~0.5.0" 1730 | content-type "^1.0.0" 1731 | cookies "~0.7.0" 1732 | debug "*" 1733 | delegates "^1.0.0" 1734 | depd "^1.1.0" 1735 | destroy "^1.0.3" 1736 | error-inject "~1.0.0" 1737 | escape-html "~1.0.1" 1738 | fresh "^0.5.2" 1739 | http-assert "^1.1.0" 1740 | http-errors "^1.2.8" 1741 | is-generator-function "^1.0.3" 1742 | koa-compose "^4.0.0" 1743 | koa-convert "^1.2.0" 1744 | koa-is-json "^1.0.0" 1745 | mime-types "^2.0.7" 1746 | on-finished "^2.1.0" 1747 | only "0.0.2" 1748 | parseurl "^1.3.0" 1749 | statuses "^1.2.0" 1750 | type-is "^1.5.5" 1751 | vary "^1.0.0" 1752 | 1753 | lazy-cache@^1.0.3: 1754 | version "1.0.4" 1755 | resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" 1756 | 1757 | lcid@^1.0.0: 1758 | version "1.0.0" 1759 | resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" 1760 | dependencies: 1761 | invert-kv "^1.0.0" 1762 | 1763 | levn@^0.3.0, levn@~0.3.0: 1764 | version "0.3.0" 1765 | resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" 1766 | dependencies: 1767 | prelude-ls "~1.1.2" 1768 | type-check "~0.3.2" 1769 | 1770 | load-json-file@^1.0.0: 1771 | version "1.1.0" 1772 | resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" 1773 | dependencies: 1774 | graceful-fs "^4.1.2" 1775 | parse-json "^2.2.0" 1776 | pify "^2.0.0" 1777 | pinkie-promise "^2.0.0" 1778 | strip-bom "^2.0.0" 1779 | 1780 | locate-path@^2.0.0: 1781 | version "2.0.0" 1782 | resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" 1783 | dependencies: 1784 | p-locate "^2.0.0" 1785 | path-exists "^3.0.0" 1786 | 1787 | lodash._baseassign@^3.0.0: 1788 | version "3.2.0" 1789 | resolved "https://registry.yarnpkg.com/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz#8c38a099500f215ad09e59f1722fd0c52bfe0a4e" 1790 | dependencies: 1791 | lodash._basecopy "^3.0.0" 1792 | lodash.keys "^3.0.0" 1793 | 1794 | lodash._basecopy@^3.0.0: 1795 | version "3.0.1" 1796 | resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36" 1797 | 1798 | lodash._basecreate@^3.0.0: 1799 | version "3.0.3" 1800 | resolved "https://registry.yarnpkg.com/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz#1bc661614daa7fc311b7d03bf16806a0213cf821" 1801 | 1802 | lodash._getnative@^3.0.0: 1803 | version "3.9.1" 1804 | resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" 1805 | 1806 | lodash._isiterateecall@^3.0.0: 1807 | version "3.0.9" 1808 | resolved "https://registry.yarnpkg.com/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz#5203ad7ba425fae842460e696db9cf3e6aac057c" 1809 | 1810 | lodash.create@3.1.1: 1811 | version "3.1.1" 1812 | resolved "https://registry.yarnpkg.com/lodash.create/-/lodash.create-3.1.1.tgz#d7f2849f0dbda7e04682bb8cd72ab022461debe7" 1813 | dependencies: 1814 | lodash._baseassign "^3.0.0" 1815 | lodash._basecreate "^3.0.0" 1816 | lodash._isiterateecall "^3.0.0" 1817 | 1818 | lodash.get@^4.4.2: 1819 | version "4.4.2" 1820 | resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" 1821 | 1822 | lodash.isarguments@^3.0.0: 1823 | version "3.1.0" 1824 | resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" 1825 | 1826 | lodash.isarray@^3.0.0: 1827 | version "3.0.4" 1828 | resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55" 1829 | 1830 | lodash.keys@^3.0.0: 1831 | version "3.1.2" 1832 | resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" 1833 | dependencies: 1834 | lodash._getnative "^3.0.0" 1835 | lodash.isarguments "^3.0.0" 1836 | lodash.isarray "^3.0.0" 1837 | 1838 | lodash@^4.0.0, lodash@^4.17.4, lodash@^4.3.0: 1839 | version "4.17.4" 1840 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" 1841 | 1842 | log4js@0.6.20: 1843 | version "0.6.20" 1844 | resolved "https://registry.yarnpkg.com/log4js/-/log4js-0.6.20.tgz#5382993d038ee2e453de21c82f4b1beeed2c48fa" 1845 | dependencies: 1846 | async "0.1.15" 1847 | readable-stream "~1.0.2" 1848 | semver "~1.1.4" 1849 | 1850 | lolex@^2.2.0, lolex@^2.3.2: 1851 | version "2.3.2" 1852 | resolved "https://registry.yarnpkg.com/lolex/-/lolex-2.3.2.tgz#85f9450425103bf9e7a60668ea25dc43274ca807" 1853 | 1854 | longest@^1.0.1: 1855 | version "1.0.1" 1856 | resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" 1857 | 1858 | loose-envify@^1.0.0: 1859 | version "1.3.1" 1860 | resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848" 1861 | dependencies: 1862 | js-tokens "^3.0.0" 1863 | 1864 | lru-cache@^4.0.1: 1865 | version "4.1.1" 1866 | resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.1.tgz#622e32e82488b49279114a4f9ecf45e7cd6bba55" 1867 | dependencies: 1868 | pseudomap "^1.0.2" 1869 | yallist "^2.1.2" 1870 | 1871 | map-cache@^0.2.2: 1872 | version "0.2.2" 1873 | resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" 1874 | 1875 | map-visit@^1.0.0: 1876 | version "1.0.0" 1877 | resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" 1878 | dependencies: 1879 | object-visit "^1.0.0" 1880 | 1881 | md5-hex@^1.2.0: 1882 | version "1.3.0" 1883 | resolved "https://registry.yarnpkg.com/md5-hex/-/md5-hex-1.3.0.tgz#d2c4afe983c4370662179b8cad145219135046c4" 1884 | dependencies: 1885 | md5-o-matic "^0.1.1" 1886 | 1887 | md5-o-matic@^0.1.1: 1888 | version "0.1.1" 1889 | resolved "https://registry.yarnpkg.com/md5-o-matic/-/md5-o-matic-0.1.1.tgz#822bccd65e117c514fab176b25945d54100a03c3" 1890 | 1891 | media-typer@0.3.0: 1892 | version "0.3.0" 1893 | resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" 1894 | 1895 | mem@^1.1.0: 1896 | version "1.1.0" 1897 | resolved "https://registry.yarnpkg.com/mem/-/mem-1.1.0.tgz#5edd52b485ca1d900fe64895505399a0dfa45f76" 1898 | dependencies: 1899 | mimic-fn "^1.0.0" 1900 | 1901 | merge-descriptors@~1.0.0: 1902 | version "1.0.1" 1903 | resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" 1904 | 1905 | merge-source-map@^1.0.2: 1906 | version "1.0.4" 1907 | resolved "https://registry.yarnpkg.com/merge-source-map/-/merge-source-map-1.0.4.tgz#a5de46538dae84d4114cc5ea02b4772a6346701f" 1908 | dependencies: 1909 | source-map "^0.5.6" 1910 | 1911 | methods@~1.1.0: 1912 | version "1.1.2" 1913 | resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" 1914 | 1915 | micromatch@^2.3.11: 1916 | version "2.3.11" 1917 | resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" 1918 | dependencies: 1919 | arr-diff "^2.0.0" 1920 | array-unique "^0.2.1" 1921 | braces "^1.8.2" 1922 | expand-brackets "^0.1.4" 1923 | extglob "^0.3.1" 1924 | filename-regex "^2.0.0" 1925 | is-extglob "^1.0.0" 1926 | is-glob "^2.0.1" 1927 | kind-of "^3.0.2" 1928 | normalize-path "^2.0.1" 1929 | object.omit "^2.0.0" 1930 | parse-glob "^3.0.4" 1931 | regex-cache "^0.4.2" 1932 | 1933 | micromatch@^3.1.8: 1934 | version "3.1.10" 1935 | resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" 1936 | dependencies: 1937 | arr-diff "^4.0.0" 1938 | array-unique "^0.3.2" 1939 | braces "^2.3.1" 1940 | define-property "^2.0.2" 1941 | extend-shallow "^3.0.2" 1942 | extglob "^2.0.4" 1943 | fragment-cache "^0.2.1" 1944 | kind-of "^6.0.2" 1945 | nanomatch "^1.2.9" 1946 | object.pick "^1.3.0" 1947 | regex-not "^1.0.0" 1948 | snapdragon "^0.8.1" 1949 | to-regex "^3.0.2" 1950 | 1951 | mime-db@~1.30.0: 1952 | version "1.30.0" 1953 | resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.30.0.tgz#74c643da2dd9d6a45399963465b26d5ca7d71f01" 1954 | 1955 | mime-db@~1.33.0: 1956 | version "1.33.0" 1957 | resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" 1958 | 1959 | mime-types@^2.0.7, mime-types@~2.1.18: 1960 | version "2.1.18" 1961 | resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" 1962 | dependencies: 1963 | mime-db "~1.33.0" 1964 | 1965 | mime-types@^2.1.12, mime-types@~2.1.17: 1966 | version "2.1.17" 1967 | resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.17.tgz#09d7a393f03e995a79f8af857b70a9e0ab16557a" 1968 | dependencies: 1969 | mime-db "~1.30.0" 1970 | 1971 | mime@^1.3.4: 1972 | version "1.4.0" 1973 | resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.0.tgz#69e9e0db51d44f2a3b56e48b7817d7d137f1a343" 1974 | 1975 | mimic-fn@^1.0.0: 1976 | version "1.1.0" 1977 | resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.1.0.tgz#e667783d92e89dbd342818b5230b9d62a672ad18" 1978 | 1979 | minimatch@^3.0.2, minimatch@^3.0.4: 1980 | version "3.0.4" 1981 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" 1982 | dependencies: 1983 | brace-expansion "^1.1.7" 1984 | 1985 | minimist@0.0.8, minimist@~0.0.1: 1986 | version "0.0.8" 1987 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" 1988 | 1989 | mixin-deep@^1.2.0: 1990 | version "1.3.1" 1991 | resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.1.tgz#a49e7268dce1a0d9698e45326c5626df3543d0fe" 1992 | dependencies: 1993 | for-in "^1.0.2" 1994 | is-extendable "^1.0.1" 1995 | 1996 | mkdirp@0.5.0: 1997 | version "0.5.0" 1998 | resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.0.tgz#1d73076a6df986cd9344e15e71fcc05a4c9abf12" 1999 | dependencies: 2000 | minimist "0.0.8" 2001 | 2002 | mkdirp@0.5.1, mkdirp@^0.5.0, mkdirp@^0.5.1: 2003 | version "0.5.1" 2004 | resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" 2005 | dependencies: 2006 | minimist "0.0.8" 2007 | 2008 | mocha@3.5.0: 2009 | version "3.5.0" 2010 | resolved "https://registry.yarnpkg.com/mocha/-/mocha-3.5.0.tgz#1328567d2717f997030f8006234bce9b8cd72465" 2011 | dependencies: 2012 | browser-stdout "1.3.0" 2013 | commander "2.9.0" 2014 | debug "2.6.8" 2015 | diff "3.2.0" 2016 | escape-string-regexp "1.0.5" 2017 | glob "7.1.1" 2018 | growl "1.9.2" 2019 | json3 "3.3.2" 2020 | lodash.create "3.1.1" 2021 | mkdirp "0.5.1" 2022 | supports-color "3.1.2" 2023 | 2024 | module-not-found-error@^1.0.0: 2025 | version "1.0.1" 2026 | resolved "https://registry.yarnpkg.com/module-not-found-error/-/module-not-found-error-1.0.1.tgz#cf8b4ff4f29640674d6cdd02b0e3bc523c2bbdc0" 2027 | 2028 | ms@0.7.1: 2029 | version "0.7.1" 2030 | resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" 2031 | 2032 | ms@2.0.0: 2033 | version "2.0.0" 2034 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" 2035 | 2036 | mute-stream@0.0.7: 2037 | version "0.0.7" 2038 | resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" 2039 | 2040 | nanomatch@^1.2.9: 2041 | version "1.2.9" 2042 | resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.9.tgz#879f7150cb2dab7a471259066c104eee6e0fa7c2" 2043 | dependencies: 2044 | arr-diff "^4.0.0" 2045 | array-unique "^0.3.2" 2046 | define-property "^2.0.2" 2047 | extend-shallow "^3.0.2" 2048 | fragment-cache "^0.2.1" 2049 | is-odd "^2.0.0" 2050 | is-windows "^1.0.2" 2051 | kind-of "^6.0.2" 2052 | object.pick "^1.3.0" 2053 | regex-not "^1.0.0" 2054 | snapdragon "^0.8.1" 2055 | to-regex "^3.0.1" 2056 | 2057 | natural-compare@^1.4.0: 2058 | version "1.4.0" 2059 | resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" 2060 | 2061 | negotiator@0.6.1: 2062 | version "0.6.1" 2063 | resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" 2064 | 2065 | nise@^1.2.0: 2066 | version "1.3.0" 2067 | resolved "https://registry.yarnpkg.com/nise/-/nise-1.3.0.tgz#7d6d506e64a0e37959495157f30a799c0436df72" 2068 | dependencies: 2069 | "@sinonjs/formatio" "^2.0.0" 2070 | just-extend "^1.1.27" 2071 | lolex "^2.3.2" 2072 | path-to-regexp "^1.7.0" 2073 | text-encoding "^0.6.4" 2074 | 2075 | normalize-package-data@^2.3.2: 2076 | version "2.4.0" 2077 | resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f" 2078 | dependencies: 2079 | hosted-git-info "^2.1.4" 2080 | is-builtin-module "^1.0.0" 2081 | semver "2 || 3 || 4 || 5" 2082 | validate-npm-package-license "^3.0.1" 2083 | 2084 | normalize-path@^2.0.1: 2085 | version "2.1.1" 2086 | resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" 2087 | dependencies: 2088 | remove-trailing-separator "^1.0.1" 2089 | 2090 | npm-run-path@^2.0.0: 2091 | version "2.0.2" 2092 | resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" 2093 | dependencies: 2094 | path-key "^2.0.0" 2095 | 2096 | number-is-nan@^1.0.0: 2097 | version "1.0.1" 2098 | resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" 2099 | 2100 | nyc@^11.5.0: 2101 | version "11.6.0" 2102 | resolved "https://registry.yarnpkg.com/nyc/-/nyc-11.6.0.tgz#d9c7b51ffceb6bba099a4683a6adc1b331b98853" 2103 | dependencies: 2104 | archy "^1.0.0" 2105 | arrify "^1.0.1" 2106 | caching-transform "^1.0.0" 2107 | convert-source-map "^1.5.1" 2108 | debug-log "^1.0.1" 2109 | default-require-extensions "^1.0.0" 2110 | find-cache-dir "^0.1.1" 2111 | find-up "^2.1.0" 2112 | foreground-child "^1.5.3" 2113 | glob "^7.0.6" 2114 | istanbul-lib-coverage "^1.1.2" 2115 | istanbul-lib-hook "^1.1.0" 2116 | istanbul-lib-instrument "^1.10.0" 2117 | istanbul-lib-report "^1.1.3" 2118 | istanbul-lib-source-maps "^1.2.3" 2119 | istanbul-reports "^1.1.4" 2120 | md5-hex "^1.2.0" 2121 | merge-source-map "^1.0.2" 2122 | micromatch "^2.3.11" 2123 | mkdirp "^0.5.0" 2124 | resolve-from "^2.0.0" 2125 | rimraf "^2.5.4" 2126 | signal-exit "^3.0.1" 2127 | spawn-wrap "^1.4.2" 2128 | test-exclude "^4.2.0" 2129 | yargs "11.1.0" 2130 | yargs-parser "^8.0.0" 2131 | 2132 | oauth-sign@~0.8.2: 2133 | version "0.8.2" 2134 | resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" 2135 | 2136 | object-assign@^4.0.1, object-assign@^4.1.0: 2137 | version "4.1.1" 2138 | resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" 2139 | 2140 | object-copy@^0.1.0: 2141 | version "0.1.0" 2142 | resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" 2143 | dependencies: 2144 | copy-descriptor "^0.1.0" 2145 | define-property "^0.2.5" 2146 | kind-of "^3.0.3" 2147 | 2148 | object-visit@^1.0.0: 2149 | version "1.0.1" 2150 | resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" 2151 | dependencies: 2152 | isobject "^3.0.0" 2153 | 2154 | object.omit@^2.0.0: 2155 | version "2.0.1" 2156 | resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" 2157 | dependencies: 2158 | for-own "^0.1.4" 2159 | is-extendable "^0.1.1" 2160 | 2161 | object.pick@^1.3.0: 2162 | version "1.3.0" 2163 | resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" 2164 | dependencies: 2165 | isobject "^3.0.1" 2166 | 2167 | on-finished@^2.1.0: 2168 | version "2.3.0" 2169 | resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" 2170 | dependencies: 2171 | ee-first "1.1.1" 2172 | 2173 | once@^1.3.0: 2174 | version "1.4.0" 2175 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 2176 | dependencies: 2177 | wrappy "1" 2178 | 2179 | onetime@^2.0.0: 2180 | version "2.0.1" 2181 | resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" 2182 | dependencies: 2183 | mimic-fn "^1.0.0" 2184 | 2185 | only@0.0.2: 2186 | version "0.0.2" 2187 | resolved "https://registry.yarnpkg.com/only/-/only-0.0.2.tgz#2afde84d03e50b9a8edc444e30610a70295edfb4" 2188 | 2189 | optimist@^0.6.1: 2190 | version "0.6.1" 2191 | resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" 2192 | dependencies: 2193 | minimist "~0.0.1" 2194 | wordwrap "~0.0.2" 2195 | 2196 | optionator@^0.8.2: 2197 | version "0.8.2" 2198 | resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" 2199 | dependencies: 2200 | deep-is "~0.1.3" 2201 | fast-levenshtein "~2.0.4" 2202 | levn "~0.3.0" 2203 | prelude-ls "~1.1.2" 2204 | type-check "~0.3.2" 2205 | wordwrap "~1.0.0" 2206 | 2207 | os-homedir@^1.0.1: 2208 | version "1.0.2" 2209 | resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" 2210 | 2211 | os-locale@^2.0.0: 2212 | version "2.1.0" 2213 | resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-2.1.0.tgz#42bc2900a6b5b8bd17376c8e882b65afccf24bf2" 2214 | dependencies: 2215 | execa "^0.7.0" 2216 | lcid "^1.0.0" 2217 | mem "^1.1.0" 2218 | 2219 | os-tmpdir@~1.0.2: 2220 | version "1.0.2" 2221 | resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" 2222 | 2223 | p-finally@^1.0.0: 2224 | version "1.0.0" 2225 | resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" 2226 | 2227 | p-limit@^1.1.0: 2228 | version "1.1.0" 2229 | resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.1.0.tgz#b07ff2d9a5d88bec806035895a2bab66a27988bc" 2230 | 2231 | p-locate@^2.0.0: 2232 | version "2.0.0" 2233 | resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" 2234 | dependencies: 2235 | p-limit "^1.1.0" 2236 | 2237 | pako@^0.2.6: 2238 | version "0.2.9" 2239 | resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75" 2240 | 2241 | parse-glob@^3.0.4: 2242 | version "3.0.4" 2243 | resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" 2244 | dependencies: 2245 | glob-base "^0.3.0" 2246 | is-dotfile "^1.0.0" 2247 | is-extglob "^1.0.0" 2248 | is-glob "^2.0.0" 2249 | 2250 | parse-json@^2.2.0: 2251 | version "2.2.0" 2252 | resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" 2253 | dependencies: 2254 | error-ex "^1.2.0" 2255 | 2256 | parseurl@^1.3.0: 2257 | version "1.3.2" 2258 | resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3" 2259 | 2260 | pascalcase@^0.1.1: 2261 | version "0.1.1" 2262 | resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" 2263 | 2264 | path-exists@^2.0.0: 2265 | version "2.1.0" 2266 | resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" 2267 | dependencies: 2268 | pinkie-promise "^2.0.0" 2269 | 2270 | path-exists@^3.0.0: 2271 | version "3.0.0" 2272 | resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" 2273 | 2274 | path-is-absolute@^1.0.0: 2275 | version "1.0.1" 2276 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 2277 | 2278 | path-is-inside@^1.0.1, path-is-inside@^1.0.2: 2279 | version "1.0.2" 2280 | resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" 2281 | 2282 | path-key@^2.0.0: 2283 | version "2.0.1" 2284 | resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" 2285 | 2286 | path-parse@^1.0.5: 2287 | version "1.0.5" 2288 | resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1" 2289 | 2290 | path-to-regexp@^1.2.0, path-to-regexp@^1.7.0: 2291 | version "1.7.0" 2292 | resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.7.0.tgz#59fde0f435badacba103a84e9d3bc64e96b9937d" 2293 | dependencies: 2294 | isarray "0.0.1" 2295 | 2296 | path-type@^1.0.0: 2297 | version "1.1.0" 2298 | resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" 2299 | dependencies: 2300 | graceful-fs "^4.1.2" 2301 | pify "^2.0.0" 2302 | pinkie-promise "^2.0.0" 2303 | 2304 | pathval@^1.0.0: 2305 | version "1.1.0" 2306 | resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.0.tgz#b942e6d4bde653005ef6b71361def8727d0645e0" 2307 | 2308 | pend@~1.2.0: 2309 | version "1.2.0" 2310 | resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" 2311 | 2312 | performance-now@^2.1.0: 2313 | version "2.1.0" 2314 | resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" 2315 | 2316 | pify@^2.0.0: 2317 | version "2.3.0" 2318 | resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" 2319 | 2320 | pinkie-promise@^2.0.0: 2321 | version "2.0.1" 2322 | resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" 2323 | dependencies: 2324 | pinkie "^2.0.0" 2325 | 2326 | pinkie@^2.0.0: 2327 | version "2.0.4" 2328 | resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" 2329 | 2330 | pkg-dir@^1.0.0: 2331 | version "1.0.0" 2332 | resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-1.0.0.tgz#7a4b508a8d5bb2d629d447056ff4e9c9314cf3d4" 2333 | dependencies: 2334 | find-up "^1.0.0" 2335 | 2336 | pluralize@^4.0.0: 2337 | version "4.0.0" 2338 | resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-4.0.0.tgz#59b708c1c0190a2f692f1c7618c446b052fd1762" 2339 | 2340 | pngjs-image@~0.11.5: 2341 | version "0.11.7" 2342 | resolved "https://registry.yarnpkg.com/pngjs-image/-/pngjs-image-0.11.7.tgz#631dd59924569fc82ffebae0d5d53f85f54dab62" 2343 | dependencies: 2344 | iconv-lite "^0.4.8" 2345 | pako "^0.2.6" 2346 | pngjs "2.3.1" 2347 | request "^2.55.0" 2348 | stream-buffers "1.0.1" 2349 | underscore "1.7.0" 2350 | 2351 | pngjs@2.3.1: 2352 | version "2.3.1" 2353 | resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-2.3.1.tgz#11d1e12b9cb64d63e30c143a330f4c1f567da85f" 2354 | 2355 | posix-character-classes@^0.1.0: 2356 | version "0.1.1" 2357 | resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" 2358 | 2359 | preceptor-core@~0.10.0: 2360 | version "0.10.0" 2361 | resolved "https://registry.yarnpkg.com/preceptor-core/-/preceptor-core-0.10.0.tgz#d906e88760c6fb92121f942b393c91dfcf7618a4" 2362 | dependencies: 2363 | log4js "0.6.20" 2364 | underscore "1.7.0" 2365 | 2366 | prelude-ls@~1.1.2: 2367 | version "1.1.2" 2368 | resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" 2369 | 2370 | preserve@^0.2.0: 2371 | version "0.2.0" 2372 | resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" 2373 | 2374 | process-nextick-args@~1.0.6: 2375 | version "1.0.7" 2376 | resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" 2377 | 2378 | progress@^2.0.0: 2379 | version "2.0.0" 2380 | resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.0.tgz#8a1be366bf8fc23db2bd23f10c6fe920b4389d1f" 2381 | 2382 | promise@6.0.0: 2383 | version "6.0.0" 2384 | resolved "https://registry.yarnpkg.com/promise/-/promise-6.0.0.tgz#456538dd4afdd25dc7d0f52a5201ed242b7c109d" 2385 | dependencies: 2386 | asap "~1.0.0" 2387 | 2388 | proxy-from-env@^1.0.0: 2389 | version "1.0.0" 2390 | resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.0.0.tgz#33c50398f70ea7eb96d21f7b817630a55791c7ee" 2391 | 2392 | proxyquire@^1.8.0: 2393 | version "1.8.0" 2394 | resolved "https://registry.yarnpkg.com/proxyquire/-/proxyquire-1.8.0.tgz#02d514a5bed986f04cbb2093af16741535f79edc" 2395 | dependencies: 2396 | fill-keys "^1.0.2" 2397 | module-not-found-error "^1.0.0" 2398 | resolve "~1.1.7" 2399 | 2400 | pseudomap@^1.0.2: 2401 | version "1.0.2" 2402 | resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" 2403 | 2404 | punycode@^1.4.1: 2405 | version "1.4.1" 2406 | resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" 2407 | 2408 | puppeteer@^0.11.0: 2409 | version "0.11.0" 2410 | resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-0.11.0.tgz#0a2b856f3c2745a5884c6dde9fb44f96663988ed" 2411 | dependencies: 2412 | debug "^2.6.8" 2413 | extract-zip "^1.6.5" 2414 | https-proxy-agent "^2.1.0" 2415 | mime "^1.3.4" 2416 | progress "^2.0.0" 2417 | proxy-from-env "^1.0.0" 2418 | rimraf "^2.6.1" 2419 | ws "^3.0.0" 2420 | 2421 | qs@~6.5.1: 2422 | version "6.5.1" 2423 | resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8" 2424 | 2425 | randomatic@^1.1.3: 2426 | version "1.1.7" 2427 | resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.7.tgz#c7abe9cc8b87c0baa876b19fde83fd464797e38c" 2428 | dependencies: 2429 | is-number "^3.0.0" 2430 | kind-of "^4.0.0" 2431 | 2432 | read-pkg-up@^1.0.1: 2433 | version "1.0.1" 2434 | resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" 2435 | dependencies: 2436 | find-up "^1.0.0" 2437 | read-pkg "^1.0.0" 2438 | 2439 | read-pkg@^1.0.0: 2440 | version "1.1.0" 2441 | resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" 2442 | dependencies: 2443 | load-json-file "^1.0.0" 2444 | normalize-package-data "^2.3.2" 2445 | path-type "^1.0.0" 2446 | 2447 | readable-stream@^2.2.2: 2448 | version "2.3.3" 2449 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c" 2450 | dependencies: 2451 | core-util-is "~1.0.0" 2452 | inherits "~2.0.3" 2453 | isarray "~1.0.0" 2454 | process-nextick-args "~1.0.6" 2455 | safe-buffer "~5.1.1" 2456 | string_decoder "~1.0.3" 2457 | util-deprecate "~1.0.1" 2458 | 2459 | readable-stream@~1.0.2: 2460 | version "1.0.34" 2461 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" 2462 | dependencies: 2463 | core-util-is "~1.0.0" 2464 | inherits "~2.0.1" 2465 | isarray "0.0.1" 2466 | string_decoder "~0.10.x" 2467 | 2468 | regenerator-runtime@^0.11.0: 2469 | version "0.11.0" 2470 | resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz#7e54fe5b5ccd5d6624ea6255c3473be090b802e1" 2471 | 2472 | regex-cache@^0.4.2: 2473 | version "0.4.4" 2474 | resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.4.tgz#75bdc58a2a1496cec48a12835bc54c8d562336dd" 2475 | dependencies: 2476 | is-equal-shallow "^0.1.3" 2477 | 2478 | regex-not@^1.0.0, regex-not@^1.0.2: 2479 | version "1.0.2" 2480 | resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" 2481 | dependencies: 2482 | extend-shallow "^3.0.2" 2483 | safe-regex "^1.1.0" 2484 | 2485 | remove-trailing-separator@^1.0.1: 2486 | version "1.1.0" 2487 | resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" 2488 | 2489 | repeat-element@^1.1.2: 2490 | version "1.1.2" 2491 | resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" 2492 | 2493 | repeat-string@^1.5.2, repeat-string@^1.6.1: 2494 | version "1.6.1" 2495 | resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" 2496 | 2497 | repeating@^2.0.0: 2498 | version "2.0.1" 2499 | resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" 2500 | dependencies: 2501 | is-finite "^1.0.0" 2502 | 2503 | request@^2.55.0: 2504 | version "2.82.0" 2505 | resolved "https://registry.yarnpkg.com/request/-/request-2.82.0.tgz#2ba8a92cd7ac45660ea2b10a53ae67cd247516ea" 2506 | dependencies: 2507 | aws-sign2 "~0.7.0" 2508 | aws4 "^1.6.0" 2509 | caseless "~0.12.0" 2510 | combined-stream "~1.0.5" 2511 | extend "~3.0.1" 2512 | forever-agent "~0.6.1" 2513 | form-data "~2.3.1" 2514 | har-validator "~5.0.3" 2515 | hawk "~6.0.2" 2516 | http-signature "~1.2.0" 2517 | is-typedarray "~1.0.0" 2518 | isstream "~0.1.2" 2519 | json-stringify-safe "~5.0.1" 2520 | mime-types "~2.1.17" 2521 | oauth-sign "~0.8.2" 2522 | performance-now "^2.1.0" 2523 | qs "~6.5.1" 2524 | safe-buffer "^5.1.1" 2525 | stringstream "~0.0.5" 2526 | tough-cookie "~2.3.2" 2527 | tunnel-agent "^0.6.0" 2528 | uuid "^3.1.0" 2529 | 2530 | require-directory@^2.1.1: 2531 | version "2.1.1" 2532 | resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" 2533 | 2534 | require-main-filename@^1.0.1: 2535 | version "1.0.1" 2536 | resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" 2537 | 2538 | require-uncached@^1.0.3: 2539 | version "1.0.3" 2540 | resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3" 2541 | dependencies: 2542 | caller-path "^0.1.0" 2543 | resolve-from "^1.0.0" 2544 | 2545 | resolve-from@^1.0.0: 2546 | version "1.0.1" 2547 | resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226" 2548 | 2549 | resolve-from@^2.0.0: 2550 | version "2.0.0" 2551 | resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-2.0.0.tgz#9480ab20e94ffa1d9e80a804c7ea147611966b57" 2552 | 2553 | resolve-url@^0.2.1: 2554 | version "0.2.1" 2555 | resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" 2556 | 2557 | resolve@~1.1.7: 2558 | version "1.1.7" 2559 | resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" 2560 | 2561 | restore-cursor@^2.0.0: 2562 | version "2.0.0" 2563 | resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" 2564 | dependencies: 2565 | onetime "^2.0.0" 2566 | signal-exit "^3.0.2" 2567 | 2568 | ret@~0.1.10: 2569 | version "0.1.15" 2570 | resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" 2571 | 2572 | right-align@^0.1.1: 2573 | version "0.1.3" 2574 | resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" 2575 | dependencies: 2576 | align-text "^0.1.1" 2577 | 2578 | rimraf@^2.2.8, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2: 2579 | version "2.6.2" 2580 | resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" 2581 | dependencies: 2582 | glob "^7.0.5" 2583 | 2584 | run-async@^2.2.0: 2585 | version "2.3.0" 2586 | resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" 2587 | dependencies: 2588 | is-promise "^2.1.0" 2589 | 2590 | rx-lite-aggregates@^4.0.8: 2591 | version "4.0.8" 2592 | resolved "https://registry.yarnpkg.com/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz#753b87a89a11c95467c4ac1626c4efc4e05c67be" 2593 | dependencies: 2594 | rx-lite "*" 2595 | 2596 | rx-lite@*, rx-lite@^4.0.8: 2597 | version "4.0.8" 2598 | resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444" 2599 | 2600 | safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: 2601 | version "5.1.1" 2602 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" 2603 | 2604 | safe-regex@^1.1.0: 2605 | version "1.1.0" 2606 | resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" 2607 | dependencies: 2608 | ret "~0.1.10" 2609 | 2610 | samsam@1.3.0: 2611 | version "1.3.0" 2612 | resolved "https://registry.yarnpkg.com/samsam/-/samsam-1.3.0.tgz#8d1d9350e25622da30de3e44ba692b5221ab7c50" 2613 | 2614 | "semver@2 || 3 || 4 || 5", semver@^5.3.0: 2615 | version "5.4.1" 2616 | resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e" 2617 | 2618 | semver@~1.1.4: 2619 | version "1.1.4" 2620 | resolved "https://registry.yarnpkg.com/semver/-/semver-1.1.4.tgz#2e5a4e72bab03472cc97f72753b4508912ef5540" 2621 | 2622 | set-blocking@^2.0.0: 2623 | version "2.0.0" 2624 | resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" 2625 | 2626 | set-value@^0.4.3: 2627 | version "0.4.3" 2628 | resolved "https://registry.yarnpkg.com/set-value/-/set-value-0.4.3.tgz#7db08f9d3d22dc7f78e53af3c3bf4666ecdfccf1" 2629 | dependencies: 2630 | extend-shallow "^2.0.1" 2631 | is-extendable "^0.1.1" 2632 | is-plain-object "^2.0.1" 2633 | to-object-path "^0.3.0" 2634 | 2635 | set-value@^2.0.0: 2636 | version "2.0.0" 2637 | resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.0.tgz#71ae4a88f0feefbbf52d1ea604f3fb315ebb6274" 2638 | dependencies: 2639 | extend-shallow "^2.0.1" 2640 | is-extendable "^0.1.1" 2641 | is-plain-object "^2.0.3" 2642 | split-string "^3.0.1" 2643 | 2644 | setprototypeof@1.0.3: 2645 | version "1.0.3" 2646 | resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.3.tgz#66567e37043eeb4f04d91bd658c0cbefb55b8e04" 2647 | 2648 | shebang-command@^1.2.0: 2649 | version "1.2.0" 2650 | resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" 2651 | dependencies: 2652 | shebang-regex "^1.0.0" 2653 | 2654 | shebang-regex@^1.0.0: 2655 | version "1.0.0" 2656 | resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" 2657 | 2658 | signal-exit@^3.0.0, signal-exit@^3.0.1, signal-exit@^3.0.2: 2659 | version "3.0.2" 2660 | resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" 2661 | 2662 | sinon-chai@^3.0.0: 2663 | version "3.0.0" 2664 | resolved "https://registry.yarnpkg.com/sinon-chai/-/sinon-chai-3.0.0.tgz#d5cbd70fa71031edd96b528e0eed4038fcc99f29" 2665 | 2666 | sinon@^4.4.2: 2667 | version "4.4.2" 2668 | resolved "https://registry.yarnpkg.com/sinon/-/sinon-4.4.2.tgz#c4c41d4bd346e1d33594daec2d5df0548334fc65" 2669 | dependencies: 2670 | "@sinonjs/formatio" "^2.0.0" 2671 | diff "^3.1.0" 2672 | lodash.get "^4.4.2" 2673 | lolex "^2.2.0" 2674 | nise "^1.2.0" 2675 | supports-color "^5.1.0" 2676 | type-detect "^4.0.5" 2677 | 2678 | slice-ansi@0.0.4: 2679 | version "0.0.4" 2680 | resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" 2681 | 2682 | slide@^1.1.5: 2683 | version "1.1.6" 2684 | resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" 2685 | 2686 | snapdragon-node@^2.0.1: 2687 | version "2.1.1" 2688 | resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" 2689 | dependencies: 2690 | define-property "^1.0.0" 2691 | isobject "^3.0.0" 2692 | snapdragon-util "^3.0.1" 2693 | 2694 | snapdragon-util@^3.0.1: 2695 | version "3.0.1" 2696 | resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" 2697 | dependencies: 2698 | kind-of "^3.2.0" 2699 | 2700 | snapdragon@^0.8.1: 2701 | version "0.8.2" 2702 | resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" 2703 | dependencies: 2704 | base "^0.11.1" 2705 | debug "^2.2.0" 2706 | define-property "^0.2.5" 2707 | extend-shallow "^2.0.1" 2708 | map-cache "^0.2.2" 2709 | source-map "^0.5.6" 2710 | source-map-resolve "^0.5.0" 2711 | use "^3.1.0" 2712 | 2713 | sntp@2.x.x: 2714 | version "2.0.2" 2715 | resolved "https://registry.yarnpkg.com/sntp/-/sntp-2.0.2.tgz#5064110f0af85f7cfdb7d6b67a40028ce52b4b2b" 2716 | dependencies: 2717 | hoek "4.x.x" 2718 | 2719 | source-map-resolve@^0.5.0: 2720 | version "0.5.1" 2721 | resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.1.tgz#7ad0f593f2281598e854df80f19aae4b92d7a11a" 2722 | dependencies: 2723 | atob "^2.0.0" 2724 | decode-uri-component "^0.2.0" 2725 | resolve-url "^0.2.1" 2726 | source-map-url "^0.4.0" 2727 | urix "^0.1.0" 2728 | 2729 | source-map-url@^0.4.0: 2730 | version "0.4.0" 2731 | resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" 2732 | 2733 | source-map@^0.4.4: 2734 | version "0.4.4" 2735 | resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b" 2736 | dependencies: 2737 | amdefine ">=0.0.4" 2738 | 2739 | source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.1: 2740 | version "0.5.7" 2741 | resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" 2742 | 2743 | spawn-wrap@^1.4.2: 2744 | version "1.4.2" 2745 | resolved "https://registry.yarnpkg.com/spawn-wrap/-/spawn-wrap-1.4.2.tgz#cff58e73a8224617b6561abdc32586ea0c82248c" 2746 | dependencies: 2747 | foreground-child "^1.5.6" 2748 | mkdirp "^0.5.0" 2749 | os-homedir "^1.0.1" 2750 | rimraf "^2.6.2" 2751 | signal-exit "^3.0.2" 2752 | which "^1.3.0" 2753 | 2754 | spdx-correct@~1.0.0: 2755 | version "1.0.2" 2756 | resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-1.0.2.tgz#4b3073d933ff51f3912f03ac5519498a4150db40" 2757 | dependencies: 2758 | spdx-license-ids "^1.0.2" 2759 | 2760 | spdx-expression-parse@~1.0.0: 2761 | version "1.0.4" 2762 | resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz#9bdf2f20e1f40ed447fbe273266191fced51626c" 2763 | 2764 | spdx-license-ids@^1.0.2: 2765 | version "1.2.2" 2766 | resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz#c9df7a3424594ade6bd11900d596696dc06bac57" 2767 | 2768 | split-string@^3.0.1, split-string@^3.0.2: 2769 | version "3.1.0" 2770 | resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" 2771 | dependencies: 2772 | extend-shallow "^3.0.0" 2773 | 2774 | sprintf-js@~1.0.2: 2775 | version "1.0.3" 2776 | resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" 2777 | 2778 | sshpk@^1.7.0: 2779 | version "1.13.1" 2780 | resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.13.1.tgz#512df6da6287144316dc4c18fe1cf1d940739be3" 2781 | dependencies: 2782 | asn1 "~0.2.3" 2783 | assert-plus "^1.0.0" 2784 | dashdash "^1.12.0" 2785 | getpass "^0.1.1" 2786 | optionalDependencies: 2787 | bcrypt-pbkdf "^1.0.0" 2788 | ecc-jsbn "~0.1.1" 2789 | jsbn "~0.1.0" 2790 | tweetnacl "~0.14.0" 2791 | 2792 | static-extend@^0.1.1: 2793 | version "0.1.2" 2794 | resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" 2795 | dependencies: 2796 | define-property "^0.2.5" 2797 | object-copy "^0.1.0" 2798 | 2799 | "statuses@>= 1.3.1 < 2", statuses@^1.2.0: 2800 | version "1.4.0" 2801 | resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087" 2802 | 2803 | stream-buffers@1.0.1: 2804 | version "1.0.1" 2805 | resolved "https://registry.yarnpkg.com/stream-buffers/-/stream-buffers-1.0.1.tgz#9a44a37555f96a5b78a5a765f0c48446cb160b8c" 2806 | 2807 | string-width@^1.0.1: 2808 | version "1.0.2" 2809 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" 2810 | dependencies: 2811 | code-point-at "^1.0.0" 2812 | is-fullwidth-code-point "^1.0.0" 2813 | strip-ansi "^3.0.0" 2814 | 2815 | string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1: 2816 | version "2.1.1" 2817 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" 2818 | dependencies: 2819 | is-fullwidth-code-point "^2.0.0" 2820 | strip-ansi "^4.0.0" 2821 | 2822 | string_decoder@~0.10.x: 2823 | version "0.10.31" 2824 | resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" 2825 | 2826 | string_decoder@~1.0.3: 2827 | version "1.0.3" 2828 | resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab" 2829 | dependencies: 2830 | safe-buffer "~5.1.0" 2831 | 2832 | stringstream@~0.0.5: 2833 | version "0.0.5" 2834 | resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" 2835 | 2836 | strip-ansi@^3.0.0, strip-ansi@^3.0.1: 2837 | version "3.0.1" 2838 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" 2839 | dependencies: 2840 | ansi-regex "^2.0.0" 2841 | 2842 | strip-ansi@^4.0.0: 2843 | version "4.0.0" 2844 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" 2845 | dependencies: 2846 | ansi-regex "^3.0.0" 2847 | 2848 | strip-bom@^2.0.0: 2849 | version "2.0.0" 2850 | resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" 2851 | dependencies: 2852 | is-utf8 "^0.2.0" 2853 | 2854 | strip-eof@^1.0.0: 2855 | version "1.0.0" 2856 | resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" 2857 | 2858 | strip-json-comments@~2.0.1: 2859 | version "2.0.1" 2860 | resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" 2861 | 2862 | supports-color@3.1.2: 2863 | version "3.1.2" 2864 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.1.2.tgz#72a262894d9d408b956ca05ff37b2ed8a6e2a2d5" 2865 | dependencies: 2866 | has-flag "^1.0.0" 2867 | 2868 | supports-color@^2.0.0: 2869 | version "2.0.0" 2870 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" 2871 | 2872 | supports-color@^3.1.2: 2873 | version "3.2.3" 2874 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" 2875 | dependencies: 2876 | has-flag "^1.0.0" 2877 | 2878 | supports-color@^4.0.0: 2879 | version "4.4.0" 2880 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.4.0.tgz#883f7ddabc165142b2a61427f3352ded195d1a3e" 2881 | dependencies: 2882 | has-flag "^2.0.0" 2883 | 2884 | supports-color@^5.1.0: 2885 | version "5.3.0" 2886 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.3.0.tgz#5b24ac15db80fa927cf5227a4a33fd3c4c7676c0" 2887 | dependencies: 2888 | has-flag "^3.0.0" 2889 | 2890 | table@^4.0.1: 2891 | version "4.0.1" 2892 | resolved "https://registry.yarnpkg.com/table/-/table-4.0.1.tgz#a8116c133fac2c61f4a420ab6cdf5c4d61f0e435" 2893 | dependencies: 2894 | ajv "^4.7.0" 2895 | ajv-keywords "^1.0.0" 2896 | chalk "^1.1.1" 2897 | lodash "^4.0.0" 2898 | slice-ansi "0.0.4" 2899 | string-width "^2.0.0" 2900 | 2901 | test-exclude@^4.2.0: 2902 | version "4.2.1" 2903 | resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-4.2.1.tgz#dfa222f03480bca69207ca728b37d74b45f724fa" 2904 | dependencies: 2905 | arrify "^1.0.1" 2906 | micromatch "^3.1.8" 2907 | object-assign "^4.1.0" 2908 | read-pkg-up "^1.0.1" 2909 | require-main-filename "^1.0.1" 2910 | 2911 | text-encoding@^0.6.4: 2912 | version "0.6.4" 2913 | resolved "https://registry.yarnpkg.com/text-encoding/-/text-encoding-0.6.4.tgz#e399a982257a276dae428bb92845cb71bdc26d19" 2914 | 2915 | text-table@~0.2.0: 2916 | version "0.2.0" 2917 | resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" 2918 | 2919 | through@^2.3.6: 2920 | version "2.3.8" 2921 | resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" 2922 | 2923 | tmp@^0.0.33: 2924 | version "0.0.33" 2925 | resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" 2926 | dependencies: 2927 | os-tmpdir "~1.0.2" 2928 | 2929 | to-fast-properties@^1.0.3: 2930 | version "1.0.3" 2931 | resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" 2932 | 2933 | to-object-path@^0.3.0: 2934 | version "0.3.0" 2935 | resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" 2936 | dependencies: 2937 | kind-of "^3.0.2" 2938 | 2939 | to-regex-range@^2.1.0: 2940 | version "2.1.1" 2941 | resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" 2942 | dependencies: 2943 | is-number "^3.0.0" 2944 | repeat-string "^1.6.1" 2945 | 2946 | to-regex@^3.0.1, to-regex@^3.0.2: 2947 | version "3.0.2" 2948 | resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" 2949 | dependencies: 2950 | define-property "^2.0.2" 2951 | extend-shallow "^3.0.2" 2952 | regex-not "^1.0.2" 2953 | safe-regex "^1.1.0" 2954 | 2955 | tough-cookie@~2.3.2: 2956 | version "2.3.3" 2957 | resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.3.tgz#0b618a5565b6dea90bf3425d04d55edc475a7561" 2958 | dependencies: 2959 | punycode "^1.4.1" 2960 | 2961 | trim-right@^1.0.1: 2962 | version "1.0.1" 2963 | resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" 2964 | 2965 | tryit@^1.0.1: 2966 | version "1.0.3" 2967 | resolved "https://registry.yarnpkg.com/tryit/-/tryit-1.0.3.tgz#393be730a9446fd1ead6da59a014308f36c289cb" 2968 | 2969 | tunnel-agent@^0.6.0: 2970 | version "0.6.0" 2971 | resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" 2972 | dependencies: 2973 | safe-buffer "^5.0.1" 2974 | 2975 | tweetnacl@^0.14.3, tweetnacl@~0.14.0: 2976 | version "0.14.5" 2977 | resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" 2978 | 2979 | type-check@~0.3.2: 2980 | version "0.3.2" 2981 | resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" 2982 | dependencies: 2983 | prelude-ls "~1.1.2" 2984 | 2985 | type-detect@^3.0.0: 2986 | version "3.0.0" 2987 | resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-3.0.0.tgz#46d0cc8553abb7b13a352b0d6dea2fd58f2d9b55" 2988 | 2989 | type-detect@^4.0.0: 2990 | version "4.0.3" 2991 | resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.3.tgz#0e3f2670b44099b0b46c284d136a7ef49c74c2ea" 2992 | 2993 | type-detect@^4.0.5: 2994 | version "4.0.8" 2995 | resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" 2996 | 2997 | type-is@^1.5.5: 2998 | version "1.6.16" 2999 | resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.16.tgz#f89ce341541c672b25ee7ae3c73dee3b2be50194" 3000 | dependencies: 3001 | media-typer "0.3.0" 3002 | mime-types "~2.1.18" 3003 | 3004 | typedarray@^0.0.6: 3005 | version "0.0.6" 3006 | resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" 3007 | 3008 | uglify-js@^2.6: 3009 | version "2.8.29" 3010 | resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd" 3011 | dependencies: 3012 | source-map "~0.5.1" 3013 | yargs "~3.10.0" 3014 | optionalDependencies: 3015 | uglify-to-browserify "~1.0.0" 3016 | 3017 | uglify-to-browserify@~1.0.0: 3018 | version "1.0.2" 3019 | resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7" 3020 | 3021 | ultron@~1.1.0: 3022 | version "1.1.0" 3023 | resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.0.tgz#b07a2e6a541a815fc6a34ccd4533baec307ca864" 3024 | 3025 | underscore@1.7.0: 3026 | version "1.7.0" 3027 | resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.7.0.tgz#6bbaf0877500d36be34ecaa584e0db9fef035209" 3028 | 3029 | union-value@^1.0.0: 3030 | version "1.0.0" 3031 | resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4" 3032 | dependencies: 3033 | arr-union "^3.1.0" 3034 | get-value "^2.0.6" 3035 | is-extendable "^0.1.1" 3036 | set-value "^0.4.3" 3037 | 3038 | universalify@^0.1.0: 3039 | version "0.1.1" 3040 | resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.1.tgz#fa71badd4437af4c148841e3b3b165f9e9e590b7" 3041 | 3042 | unset-value@^1.0.0: 3043 | version "1.0.0" 3044 | resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" 3045 | dependencies: 3046 | has-value "^0.3.1" 3047 | isobject "^3.0.0" 3048 | 3049 | urix@^0.1.0: 3050 | version "0.1.0" 3051 | resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" 3052 | 3053 | use@^3.1.0: 3054 | version "3.1.0" 3055 | resolved "https://registry.yarnpkg.com/use/-/use-3.1.0.tgz#14716bf03fdfefd03040aef58d8b4b85f3a7c544" 3056 | dependencies: 3057 | kind-of "^6.0.2" 3058 | 3059 | util-deprecate@~1.0.1: 3060 | version "1.0.2" 3061 | resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" 3062 | 3063 | uuid@^3.1.0: 3064 | version "3.1.0" 3065 | resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04" 3066 | 3067 | validate-npm-package-license@^3.0.1: 3068 | version "3.0.1" 3069 | resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz#2804babe712ad3379459acfbe24746ab2c303fbc" 3070 | dependencies: 3071 | spdx-correct "~1.0.0" 3072 | spdx-expression-parse "~1.0.0" 3073 | 3074 | vary@^1.0.0: 3075 | version "1.1.2" 3076 | resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" 3077 | 3078 | verror@1.10.0: 3079 | version "1.10.0" 3080 | resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" 3081 | dependencies: 3082 | assert-plus "^1.0.0" 3083 | core-util-is "1.0.2" 3084 | extsprintf "^1.2.0" 3085 | 3086 | which-module@^2.0.0: 3087 | version "2.0.0" 3088 | resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" 3089 | 3090 | which@^1.2.9, which@^1.3.0: 3091 | version "1.3.0" 3092 | resolved "https://registry.yarnpkg.com/which/-/which-1.3.0.tgz#ff04bdfc010ee547d780bec38e1ac1c2777d253a" 3093 | dependencies: 3094 | isexe "^2.0.0" 3095 | 3096 | window-size@0.1.0: 3097 | version "0.1.0" 3098 | resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" 3099 | 3100 | wordwrap@0.0.2: 3101 | version "0.0.2" 3102 | resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" 3103 | 3104 | wordwrap@~0.0.2: 3105 | version "0.0.3" 3106 | resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" 3107 | 3108 | wordwrap@~1.0.0: 3109 | version "1.0.0" 3110 | resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" 3111 | 3112 | wrap-ansi@^2.0.0: 3113 | version "2.1.0" 3114 | resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" 3115 | dependencies: 3116 | string-width "^1.0.1" 3117 | strip-ansi "^3.0.1" 3118 | 3119 | wrappy@1: 3120 | version "1.0.2" 3121 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 3122 | 3123 | write-file-atomic@^1.1.4: 3124 | version "1.3.4" 3125 | resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-1.3.4.tgz#f807a4f0b1d9e913ae7a48112e6cc3af1991b45f" 3126 | dependencies: 3127 | graceful-fs "^4.1.11" 3128 | imurmurhash "^0.1.4" 3129 | slide "^1.1.5" 3130 | 3131 | write@^0.2.1: 3132 | version "0.2.1" 3133 | resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757" 3134 | dependencies: 3135 | mkdirp "^0.5.1" 3136 | 3137 | ws@^3.0.0: 3138 | version "3.2.0" 3139 | resolved "https://registry.yarnpkg.com/ws/-/ws-3.2.0.tgz#d5d3d6b11aff71e73f808f40cc69d52bb6d4a185" 3140 | dependencies: 3141 | async-limiter "~1.0.0" 3142 | safe-buffer "~5.1.0" 3143 | ultron "~1.1.0" 3144 | 3145 | y18n@^3.2.1: 3146 | version "3.2.1" 3147 | resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" 3148 | 3149 | yallist@^2.1.2: 3150 | version "2.1.2" 3151 | resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" 3152 | 3153 | yargs-parser@^8.0.0: 3154 | version "8.1.0" 3155 | resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-8.1.0.tgz#f1376a33b6629a5d063782944da732631e966950" 3156 | dependencies: 3157 | camelcase "^4.1.0" 3158 | 3159 | yargs-parser@^9.0.2: 3160 | version "9.0.2" 3161 | resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-9.0.2.tgz#9ccf6a43460fe4ed40a9bb68f48d43b8a68cc077" 3162 | dependencies: 3163 | camelcase "^4.1.0" 3164 | 3165 | yargs@11.1.0: 3166 | version "11.1.0" 3167 | resolved "https://registry.yarnpkg.com/yargs/-/yargs-11.1.0.tgz#90b869934ed6e871115ea2ff58b03f4724ed2d77" 3168 | dependencies: 3169 | cliui "^4.0.0" 3170 | decamelize "^1.1.1" 3171 | find-up "^2.1.0" 3172 | get-caller-file "^1.0.1" 3173 | os-locale "^2.0.0" 3174 | require-directory "^2.1.1" 3175 | require-main-filename "^1.0.1" 3176 | set-blocking "^2.0.0" 3177 | string-width "^2.0.0" 3178 | which-module "^2.0.0" 3179 | y18n "^3.2.1" 3180 | yargs-parser "^9.0.2" 3181 | 3182 | yargs@~3.10.0: 3183 | version "3.10.0" 3184 | resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1" 3185 | dependencies: 3186 | camelcase "^1.0.2" 3187 | cliui "^2.1.0" 3188 | decamelize "^1.0.0" 3189 | window-size "0.1.0" 3190 | 3191 | yauzl@2.4.1: 3192 | version "2.4.1" 3193 | resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.4.1.tgz#9528f442dab1b2284e58b4379bb194e22e0c4005" 3194 | dependencies: 3195 | fd-slicer "~1.0.1" 3196 | --------------------------------------------------------------------------------