1
0
Fork 0
mirror of https://github.com/codex-team/codex.docs.git synced 2025-07-19 13:19:42 +02:00

Move testing to 3001 port (#8)

Create separate database for testing
Add runtime configuration file support
This commit is contained in:
George Berezhnoy 2018-10-07 19:15:10 +03:00 committed by GitHub
parent 073772c047
commit 452d0ae816
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 547 additions and 36 deletions

9
.codexdocsrc Normal file
View file

@ -0,0 +1,9 @@
{
"title": "CodeX Editor   🤩🧦🤨",
"menu": [
"Guides",
"API",
"Plugins",
{"title": "Support Project", "uri": "/support"}
]
}

1
.gitignore vendored
View file

@ -65,6 +65,7 @@ typings/
# Database files # Database files
.db/ .db/
.testdb/
# Cache of babel and others # Cache of babel and others
.cache/ .cache/

13
bin/www
View file

@ -6,11 +6,12 @@
const app = require('../src/app'); const app = require('../src/app');
const debug = require('debug')('codex.editor.docs:server'); const debug = require('debug')('codex.editor.docs:server');
const http = require('http'); const http = require('http');
const config = require('../config');
/** /**
* Get port from environment and store in Express. * Get port from environment and store in Express.
*/ */
const port = normalizePort(process.env.PORT || '3000'); const port = normalizePort(config.port || '3000');
app.set('port', port); app.set('port', port);
@ -30,16 +31,16 @@ server.on('listening', onListening);
* Normalize a port into a number, string, or false. * Normalize a port into a number, string, or false.
*/ */
function normalizePort(val) { function normalizePort(val) {
const port = parseInt(val, 10); const value = parseInt(val, 10);
if (isNaN(port)) { if (isNaN(value)) {
// named pipe // named pipe
return val; return val;
} }
if (port >= 0) { if (value >= 0) {
// port number // port number
return port; return value;
} }
return false; return false;
@ -62,11 +63,9 @@ function onError(error) {
case 'EACCES': case 'EACCES':
console.error(bind + ' requires elevated privileges'); console.error(bind + ' requires elevated privileges');
process.exit(1); process.exit(1);
break;
case 'EADDRINUSE': case 'EADDRINUSE':
console.error(bind + ' is already in use'); console.error(bind + ' is already in use');
process.exit(1); process.exit(1);
break;
default: default:
throw error; throw error;
} }

4
config/development.json Normal file
View file

@ -0,0 +1,4 @@
{
"port": 3000,
"database": ".db"
}

22
config/index.js Normal file
View file

@ -0,0 +1,22 @@
/**
* This module reads configuration file depending on NODE_ENV
*
* @type {module}
*/
const fs = require('fs');
const path = require('path');
const NODE_ENV = process.env.NODE_ENV || 'development';
const configPath = `./${NODE_ENV}.json`;
let config;
if (fs.existsSync(path.resolve(__dirname, configPath))) {
config = require(configPath);
} else {
config = {
database: '.db',
port: 3000
};
}
module.exports = config;

4
config/production.json Normal file
View file

@ -0,0 +1,4 @@
{
"port": 3000,
"database": ".db"
}

5
config/testing.json Normal file
View file

@ -0,0 +1,5 @@
{
"port": 3001,
"database": ".testdb",
"rcFile": "./test/.codexdocsrc"
}

View file

@ -3,8 +3,9 @@
"version": "0.0.0", "version": "0.0.0",
"private": true, "private": true,
"scripts": { "scripts": {
"start": "nodemon ./bin/www", "start": "cross-env NODE_ENV=production nodemon ./bin/www",
"test": "mocha --recursive ./test", "start:dev": "cross-env NODE_ENV=development nodemon ./bin/www",
"test": "cross-env NODE_ENV=testing mocha --recursive ./test",
"lint": "eslint --fix --cache ./src/**/*.js", "lint": "eslint --fix --cache ./src/**/*.js",
"build": "webpack ./src/frontend/js/app.js --o='./public/dist/[name].bundle.js' --output-library=Docs --output-public-path=/dist/ -p --watch", "build": "webpack ./src/frontend/js/app.js --o='./public/dist/[name].bundle.js' --output-library=Docs --output-public-path=/dist/ -p --watch",
"precommit": "yarn lint && yarn test --exit" "precommit": "yarn lint && yarn test --exit"
@ -36,6 +37,7 @@
"babel-loader": "^8.0.2", "babel-loader": "^8.0.2",
"chai": "^4.1.2", "chai": "^4.1.2",
"chai-http": "^4.0.0", "chai-http": "^4.0.0",
"cross-env": "^5.2.0",
"css-loader": "^1.0.0", "css-loader": "^1.0.0",
"cssnano": "^4.1.0", "cssnano": "^4.1.0",
"eslint": "^5.3.0", "eslint": "^5.3.0",
@ -44,6 +46,7 @@
"husky": "^0.14.3", "husky": "^0.14.3",
"mini-css-extract-plugin": "^0.4.3", "mini-css-extract-plugin": "^0.4.3",
"mocha": "^5.2.0", "mocha": "^5.2.0",
"mocha-sinon": "^2.1.0",
"nyc": "^12.0.2", "nyc": "^12.0.2",
"postcss": "^7.0.2", "postcss": "^7.0.2",
"postcss-apply": "^0.11.0", "postcss-apply": "^0.11.0",
@ -59,6 +62,8 @@
"postcss-nested-ancestors": "^2.0.0", "postcss-nested-ancestors": "^2.0.0",
"postcss-nesting": "^6.0.0", "postcss-nesting": "^6.0.0",
"postcss-smart-import": "^0.7.6", "postcss-smart-import": "^0.7.6",
"rimraf": "^2.6.2",
"sinon": "^6.3.5",
"webpack": "^4.17.1", "webpack": "^4.17.1",
"webpack-cli": "^3.1.0" "webpack-cli": "^3.1.0"
} }

View file

@ -3,10 +3,14 @@ const express = require('express');
const path = require('path'); const path = require('path');
const cookieParser = require('cookie-parser'); const cookieParser = require('cookie-parser');
const logger = require('morgan'); const logger = require('morgan');
const rcParser = require('./utils/rcparser');
const routes = require('./routes'); const routes = require('./routes');
const app = express(); const app = express();
const config = rcParser.getConfiguration();
app.locals.config = config;
// view engine setup // view engine setup
app.set('views', path.join(__dirname, 'views')); app.set('views', path.join(__dirname, 'views'));

View file

@ -1,5 +1,6 @@
const Datastore = require('nedb'); const Datastore = require('nedb');
const config = require('../../../config');
const db = new Datastore({filename: './.db/pages.db', autoload: true}); const db = new Datastore({filename: `./${config.database}/pages.db`, autoload: true});
module.exports = db; module.exports = db;

102
src/utils/rcparser.js Normal file
View file

@ -0,0 +1,102 @@
const fs = require('fs');
const path = require('path');
const config = require('../../config');
const rcPath = path.resolve(__dirname, '../../', config.rcFile || './.codexdocsrc');
/**
* @typedef {Object} RCData
* @property {string} title - website title
* @property {object[]} menu - options for website menu
* @property {string} menu[].title - menu option title
* @property {string} menu[].uri - menu option href
*/
/**
* @class RCParser
* @classdesc Class to parse runtime configuration file for CodeX Docs engine
*/
module.exports = class RCParser {
/**
* Default CodeX Docs configuration
*
* @static
* @return {{title: string, menu: Array}}
*/
static get DEFAULTS() {
return {
title: 'CodeX Docs',
menu: []
};
}
/**
* Find and parse runtime configuration file
*
* @static
* @return {{title: string, menu: []}}
*/
static getConfiguration() {
if (!fs.existsSync(rcPath)) {
return RCParser.DEFAULTS;
}
const file = fs.readFileSync(rcPath, {encoding: 'UTF-8'});
const rConfig = {};
let userConfig;
try {
userConfig = JSON.parse(file);
} catch (e) {
console.log('CodeX Docs rc file should be in JSON format.');
return RCParser.DEFAULTS;
}
rConfig.title = userConfig.title || RCParser.DEFAULTS.title;
rConfig.menu = userConfig.menu || RCParser.DEFAULTS.menu;
if (!(rConfig.menu instanceof Array)) {
console.log('Menu section in the rc file must be an array.');
rConfig.menu = RCParser.DEFAULTS.menu;
}
rConfig.menu = rConfig.menu.filter((option, i) => {
i = i + 1;
if (typeof option === 'string') {
return true;
}
if (!option || option instanceof Array || typeof option !== 'object') {
console.log(`Menu option #${i} in rc file must be a string or an object`);
return false;
}
const {title, uri} = option;
if (!title || typeof title !== 'string') {
console.log(`Menu option #${i} title must be a string.`);
return false;
}
if (!uri || typeof uri !== 'string') {
console.log(`Menu option #${i} uri must be a string.`);
return false;
}
return true;
});
rConfig.menu = rConfig.menu.map(option => {
if (typeof option === 'string') {
return {
title: option,
/* Replace all non alpha- and numeric-symbols with '-' */
uri: '/' + option.toLowerCase().replace(/[ -/:-@[-`{-~]+/, '-')
};
}
return option;
});
return rConfig;
}
};

View file

@ -1,6 +1,6 @@
<header class="docs-header"> <header class="docs-header">
<a href="/" class="docs-header__logo"> <a href="/" class="docs-header__logo">
CodeX Editor &nbsp; 🤩🧦🤨 {{ config.title }}
</a> </a>
<ul class="docs-header__menu"> <ul class="docs-header__menu">
<li> <li>
@ -9,25 +9,12 @@
Add Page Add Page
</a> </a>
</li> </li>
{% for option in config.menu %}
<li> <li>
<a href=""> <a href="{{option.uri}}">
Guides {{ option.title }}
</a>
</li>
<li>
<a href="">
API
</a>
</li>
<li>
<a href="">
Plugins
</a>
</li>
<li>
<a href="">
Support Project
</a> </a>
</li> </li>
{% endfor %}
</ul> </ul>
</header> </header>

View file

@ -1,11 +1,12 @@
const fs = require('fs'); const fs = require('fs');
const config = require('../config');
const {expect} = require('chai'); const {expect} = require('chai');
const {class: Database} = require('../src/utils/database'); const {class: Database} = require('../src/utils/database');
const Datastore = require('nedb'); const Datastore = require('nedb');
describe('Database', () => { describe('Database', () => {
const pathToDB = './.db/test.db'; const pathToDB = `./${config.database}/test.db`;
let nedbInstance; let nedbInstance;
let db; let db;

View file

@ -1,8 +1,19 @@
const {expect} = require('chai'); const {expect} = require('chai');
const fs = require('fs');
const path = require('path');
const config = require('../../config');
const Page = require('../../src/models/page'); const Page = require('../../src/models/page');
const {pages} = require('../../src/utils/database'); const {pages} = require('../../src/utils/database');
describe('Page model', () => { describe('Page model', () => {
after(() => {
const pathToDB = path.resolve(__dirname, '../../', config.database, './pages.db');
if (fs.existsSync(pathToDB)) {
fs.unlinkSync(pathToDB);
}
});
it('Working with empty model', async () => { it('Working with empty model', async () => {
let page = new Page(); let page = new Page();

262
test/rcparser.js Normal file
View file

@ -0,0 +1,262 @@
const {expect} = require('chai');
require('mocha-sinon');
const fs = require('fs');
const path = require('path');
const config = require('../config');
const rcParser = require('../src/utils/rcparser');
const rcPath = path.resolve(process.cwd(), config.rcFile);
describe('RC file parser test', () => {
beforeEach(function () {
this.sinon.stub(console, 'log');
});
afterEach(() => {
if (fs.existsSync(rcPath)) {
fs.unlinkSync(rcPath);
}
});
it('Default config', async () => {
const parsedConfig = rcParser.getConfiguration();
expect(parsedConfig).to.be.deep.equal(rcParser.DEFAULTS);
});
it('Invalid JSON formatted config', () => {
const invalidJson = '{title: "Codex Docs"}';
fs.writeFileSync(rcPath, invalidJson, 'utf8');
const parsedConfig = rcParser.getConfiguration();
expect(console.log.calledOnce).to.be.true;
expect(console.log.calledWith('CodeX Docs rc file should be in JSON format.')).to.be.true;
expect(parsedConfig).to.be.deep.equal(rcParser.DEFAULTS);
});
it('Normal config', () => {
const normalConfig = {
title: 'Documentation',
menu: [
{title: 'Option 1', uri: '/option1'},
{title: 'Option 2', uri: '/option2'},
{title: 'Option 3', uri: '/option3'}
]
};
fs.writeFileSync(rcPath, JSON.stringify(normalConfig), 'utf8');
const parsedConfig = rcParser.getConfiguration();
expect(parsedConfig).to.be.deep.equal(normalConfig);
});
it('Missed title', () => {
const normalConfig = {
menu: [
{title: 'Option 1', uri: '/option1'},
{title: 'Option 2', uri: '/option2'},
{title: 'Option 3', uri: '/option3'}
]
};
fs.writeFileSync(rcPath, JSON.stringify(normalConfig), 'utf8');
const parsedConfig = rcParser.getConfiguration();
expect(parsedConfig.menu).to.be.deep.equal(normalConfig.menu);
expect(parsedConfig.title).to.be.equal(rcParser.DEFAULTS.title);
});
it('Missed menu', () => {
const normalConfig = {
title: 'Documentation'
};
fs.writeFileSync(rcPath, JSON.stringify(normalConfig), 'utf8');
const parsedConfig = rcParser.getConfiguration();
expect(parsedConfig.title).to.be.equal(normalConfig.title);
expect(parsedConfig.menu).to.be.deep.equal(rcParser.DEFAULTS.menu);
});
it('Menu is not an array', () => {
const normalConfig = {
title: 'Documentation',
menu: {
0: {title: 'Option 1', uri: '/option1'},
1: {title: 'Option 2', uri: '/option2'},
2: {title: 'Option 3', uri: '/option3'}
}
};
fs.writeFileSync(rcPath, JSON.stringify(normalConfig), 'utf8');
const parsedConfig = rcParser.getConfiguration();
expect(console.log.calledOnce).to.be.true;
expect(console.log.calledWith('Menu section in the rc file must be an array.')).to.be.true;
expect(parsedConfig.title).to.be.equal(normalConfig.title);
expect(parsedConfig.menu).to.be.deep.equal(rcParser.DEFAULTS.menu);
});
it('Menu option is a string', () => {
const normalConfig = {
title: 'Documentation',
menu: [
'Option 1',
{title: 'Option 2', uri: '/option2'},
{title: 'Option 3', uri: '/option3'}
]
};
const expectedMenu = [
{title: 'Option 1', uri: '/option-1'},
{title: 'Option 2', uri: '/option2'},
{title: 'Option 3', uri: '/option3'}
];
fs.writeFileSync(rcPath, JSON.stringify(normalConfig), 'utf8');
const parsedConfig = rcParser.getConfiguration();
expect(parsedConfig.title).to.be.equal(normalConfig.title);
expect(parsedConfig.menu).to.be.deep.equal(expectedMenu);
});
it('Menu option is not a string or an object', () => {
const normalConfig = {
title: 'Documentation',
menu: [
[ {title: 'Option 1', uri: '/option1'} ],
{title: 'Option 2', uri: '/option2'},
{title: 'Option 3', uri: '/option3'}
]
};
const expectedMenu = [
{title: 'Option 2', uri: '/option2'},
{title: 'Option 3', uri: '/option3'}
];
fs.writeFileSync(rcPath, JSON.stringify(normalConfig), 'utf8');
const parsedConfig = rcParser.getConfiguration();
expect(console.log.calledOnce).to.be.true;
expect(console.log.calledWith('Menu option #1 in rc file must be a string or an object')).to.be.true;
expect(parsedConfig.title).to.be.equal(normalConfig.title);
expect(parsedConfig.menu).to.be.deep.equal(expectedMenu);
});
it('Menu option title is undefined', () => {
const normalConfig = {
title: 'Documentation',
menu: [
{uri: '/option1'},
{title: 'Option 2', uri: '/option2'},
{title: 'Option 3', uri: '/option3'}
]
};
const expectedMenu = [
{title: 'Option 2', uri: '/option2'},
{title: 'Option 3', uri: '/option3'}
];
fs.writeFileSync(rcPath, JSON.stringify(normalConfig), 'utf8');
const parsedConfig = rcParser.getConfiguration();
expect(console.log.calledOnce).to.be.true;
expect(console.log.calledWith('Menu option #1 title must be a string.')).to.be.true;
expect(parsedConfig.title).to.be.equal(normalConfig.title);
expect(parsedConfig.menu).to.be.deep.equal(expectedMenu);
});
it('Menu option title is not a string', () => {
const normalConfig = {
title: 'Documentation',
menu: [
{title: [], uri: '/option1'},
{title: 'Option 2', uri: '/option2'},
{title: 'Option 3', uri: '/option3'}
]
};
const expectedMenu = [
{title: 'Option 2', uri: '/option2'},
{title: 'Option 3', uri: '/option3'}
];
fs.writeFileSync(rcPath, JSON.stringify(normalConfig), 'utf8');
const parsedConfig = rcParser.getConfiguration();
expect(console.log.calledOnce).to.be.true;
expect(console.log.calledWith('Menu option #1 title must be a string.')).to.be.true;
expect(parsedConfig.title).to.be.equal(normalConfig.title);
expect(parsedConfig.menu).to.be.deep.equal(expectedMenu);
});
it('Menu option uri is undefined', () => {
const normalConfig = {
title: 'Documentation',
menu: [
{title: 'Option 1'},
{title: 'Option 2', uri: '/option2'},
{title: 'Option 3', uri: '/option3'}
]
};
const expectedMenu = [
{title: 'Option 2', uri: '/option2'},
{title: 'Option 3', uri: '/option3'}
];
fs.writeFileSync(rcPath, JSON.stringify(normalConfig), 'utf8');
const parsedConfig = rcParser.getConfiguration();
expect(console.log.calledOnce).to.be.true;
expect(console.log.calledWith('Menu option #1 uri must be a string.')).to.be.true;
expect(parsedConfig.title).to.be.equal(normalConfig.title);
expect(parsedConfig.menu).to.be.deep.equal(expectedMenu);
});
it('Menu option title is not a string', () => {
const normalConfig = {
title: 'Documentation',
menu: [
{title: 'Option 1', uri: []},
{title: 'Option 2', uri: '/option2'},
{title: 'Option 3', uri: '/option3'}
]
};
const expectedMenu = [
{title: 'Option 2', uri: '/option2'},
{title: 'Option 3', uri: '/option3'}
];
fs.writeFileSync(rcPath, JSON.stringify(normalConfig), 'utf8');
const parsedConfig = rcParser.getConfiguration();
expect(console.log.calledOnce).to.be.true;
expect(console.log.calledWith('Menu option #1 uri must be a string.')).to.be.true;
expect(parsedConfig.title).to.be.equal(normalConfig.title);
expect(parsedConfig.menu).to.be.deep.equal(expectedMenu);
});
});

View file

@ -1,6 +1,9 @@
const {app} = require('../../bin/www'); const {app} = require('../../bin/www');
const model = require('../../src/models/page'); const model = require('../../src/models/page');
const fs = require('fs');
const path = require('path');
const config = require('../../config');
const chai = require('chai'); const chai = require('chai');
const chaiHTTP = require('chai-http'); const chaiHTTP = require('chai-http');
const {expect} = chai; const {expect} = chai;
@ -14,6 +17,14 @@ describe('Pages REST: ', () => {
agent = chai.request.agent(app); agent = chai.request.agent(app);
}); });
after(async () => {
const pathToDB = path.resolve(__dirname, '../../', config.database, './pages.db');
if (fs.existsSync(pathToDB)) {
fs.unlinkSync(pathToDB);
}
});
it('Creating page', async () => { it('Creating page', async () => {
const body = { const body = {
blocks: [ blocks: [

View file

@ -615,6 +615,28 @@
version "1.4.0" version "1.4.0"
resolved "https://registry.yarnpkg.com/@csstools/convert-colors/-/convert-colors-1.4.0.tgz#ad495dc41b12e75d588c6db8b9834f08fa131eb7" resolved "https://registry.yarnpkg.com/@csstools/convert-colors/-/convert-colors-1.4.0.tgz#ad495dc41b12e75d588c6db8b9834f08fa131eb7"
"@sinonjs/commons@^1.0.2":
version "1.0.2"
resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.0.2.tgz#3e0ac737781627b8844257fadc3d803997d0526e"
dependencies:
type-detect "4.0.8"
"@sinonjs/formatio@3.0.0", "@sinonjs/formatio@^3.0.0":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@sinonjs/formatio/-/formatio-3.0.0.tgz#9d282d81030a03a03fa0c5ce31fd8786a4da311a"
dependencies:
"@sinonjs/samsam" "2.1.0"
"@sinonjs/samsam@2.1.0":
version "2.1.0"
resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-2.1.0.tgz#b8b8f5b819605bd63601a6ede459156880f38ea3"
dependencies:
array-from "^2.1.1"
"@sinonjs/samsam@^2.1.2":
version "2.1.2"
resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-2.1.2.tgz#16947fce5f57258d01f1688fdc32723093c55d3f"
"@types/chai@4": "@types/chai@4":
version "4.1.4" version "4.1.4"
resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.1.4.tgz#5ca073b330d90b4066d6ce18f60d57f2084ce8ca" resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.1.4.tgz#5ca073b330d90b4066d6ce18f60d57f2084ce8ca"
@ -911,6 +933,10 @@ array-flatten@1.1.1:
version "1.1.1" version "1.1.1"
resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
array-from@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/array-from/-/array-from-2.1.1.tgz#cfe9d8c26628b9dc5aecc62a9f5d8f1f352c1195"
array-union@^1.0.1: array-union@^1.0.1:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39"
@ -1678,6 +1704,13 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4:
safe-buffer "^5.0.1" safe-buffer "^5.0.1"
sha.js "^2.4.8" sha.js "^2.4.8"
cross-env@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-5.2.0.tgz#6ecd4c015d5773e614039ee529076669b9d126f2"
dependencies:
cross-spawn "^6.0.5"
is-windows "^1.0.0"
cross-spawn@^4: cross-spawn@^4:
version "4.0.2" version "4.0.2"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-4.0.2.tgz#7b9247621c23adfdd3856004a823cbe397424d41" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-4.0.2.tgz#7b9247621c23adfdd3856004a823cbe397424d41"
@ -2009,7 +2042,7 @@ dicer@0.2.5:
readable-stream "1.1.x" readable-stream "1.1.x"
streamsearch "0.1.2" streamsearch "0.1.2"
diff@3.5.0: diff@3.5.0, diff@^3.5.0:
version "3.5.0" version "3.5.0"
resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12"
@ -3372,7 +3405,7 @@ is-utf8@^0.2.0:
version "0.2.1" version "0.2.1"
resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72"
is-windows@^1.0.2: is-windows@^1.0.0, is-windows@^1.0.2:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d"
@ -3492,6 +3525,10 @@ json5@^0.5.0:
version "0.5.1" version "0.5.1"
resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821"
just-extend@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-3.0.0.tgz#cee004031eaabf6406da03a7b84e4fe9d78ef288"
kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0:
version "3.2.2" version "3.2.2"
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64"
@ -3606,6 +3643,10 @@ lodash.debounce@^4.0.8:
version "4.0.8" version "4.0.8"
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
lodash.get@^4.4.2:
version "4.4.2"
resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
lodash.memoize@^4.1.2: lodash.memoize@^4.1.2:
version "4.1.2" version "4.1.2"
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
@ -3618,6 +3659,10 @@ lodash@^4.17.10, lodash@^4.17.4, lodash@^4.17.5:
version "4.17.11" version "4.17.11"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
lolex@^2.3.2, lolex@^2.7.5:
version "2.7.5"
resolved "https://registry.yarnpkg.com/lolex/-/lolex-2.7.5.tgz#113001d56bfc7e02d56e36291cc5c413d1aa0733"
loose-envify@^1.0.0: loose-envify@^1.0.0:
version "1.4.0" version "1.4.0"
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
@ -3873,6 +3918,10 @@ mkdirp@0.5.1, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1:
dependencies: dependencies:
minimist "0.0.8" minimist "0.0.8"
mocha-sinon@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/mocha-sinon/-/mocha-sinon-2.1.0.tgz#61e92727e577bee44cac6f32162dcec33c1301e5"
mocha@^5.2.0: mocha@^5.2.0:
version "5.2.0" version "5.2.0"
resolved "https://registry.yarnpkg.com/mocha/-/mocha-5.2.0.tgz#6d8ae508f59167f940f2b5b3c4a612ae50c90ae6" resolved "https://registry.yarnpkg.com/mocha/-/mocha-5.2.0.tgz#6d8ae508f59167f940f2b5b3c4a612ae50c90ae6"
@ -3993,6 +4042,16 @@ nice-try@^1.0.4:
version "1.0.5" version "1.0.5"
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
nise@^1.4.5:
version "1.4.5"
resolved "https://registry.yarnpkg.com/nise/-/nise-1.4.5.tgz#979a97a19c48d627bb53703726ae8d53ce8d4b3e"
dependencies:
"@sinonjs/formatio" "3.0.0"
just-extend "^3.0.0"
lolex "^2.3.2"
path-to-regexp "^1.7.0"
text-encoding "^0.6.4"
node-libs-browser@^2.0.0: node-libs-browser@^2.0.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.1.0.tgz#5f94263d404f6e44767d726901fff05478d600df" resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.1.0.tgz#5f94263d404f6e44767d726901fff05478d600df"
@ -4446,6 +4505,12 @@ path-to-regexp@0.1.7:
version "0.1.7" version "0.1.7"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
path-to-regexp@^1.7.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.7.0.tgz#59fde0f435badacba103a84e9d3bc64e96b9937d"
dependencies:
isarray "0.0.1"
path-type@^1.0.0: path-type@^1.0.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441"
@ -5530,6 +5595,20 @@ simple-swizzle@^0.2.2:
dependencies: dependencies:
is-arrayish "^0.3.1" is-arrayish "^0.3.1"
sinon@^6.3.5:
version "6.3.5"
resolved "https://registry.yarnpkg.com/sinon/-/sinon-6.3.5.tgz#0f6d6a5b4ebaad1f6e8e019395542d1d02c144a0"
dependencies:
"@sinonjs/commons" "^1.0.2"
"@sinonjs/formatio" "^3.0.0"
"@sinonjs/samsam" "^2.1.2"
diff "^3.5.0"
lodash.get "^4.4.2"
lolex "^2.7.5"
nise "^1.4.5"
supports-color "^5.5.0"
type-detect "^4.0.8"
slice-ansi@1.0.0: slice-ansi@1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-1.0.0.tgz#044f1a49d8842ff307aad6b505ed178bd950134d" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-1.0.0.tgz#044f1a49d8842ff307aad6b505ed178bd950134d"
@ -5810,7 +5889,7 @@ supports-color@^3.1.2:
dependencies: dependencies:
has-flag "^1.0.0" has-flag "^1.0.0"
supports-color@^5.2.0, supports-color@^5.3.0, supports-color@^5.4.0: supports-color@^5.2.0, supports-color@^5.3.0, supports-color@^5.4.0, supports-color@^5.5.0:
version "5.5.0" version "5.5.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
dependencies: dependencies:
@ -5878,6 +5957,10 @@ test-exclude@^4.2.0:
read-pkg-up "^1.0.1" read-pkg-up "^1.0.1"
require-main-filename "^1.0.1" require-main-filename "^1.0.1"
text-encoding@^0.6.4:
version "0.6.4"
resolved "http://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz#e399a982257a276dae428bb92845cb71bdc26d19"
text-table@^0.2.0: text-table@^0.2.0:
version "0.2.0" version "0.2.0"
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
@ -5975,7 +6058,7 @@ type-check@~0.3.2:
dependencies: dependencies:
prelude-ls "~1.1.2" prelude-ls "~1.1.2"
type-detect@^4.0.0: type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.8:
version "4.0.8" version "4.0.8"
resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"