Add Browserstack for IE11 testing (#6557)

* add browserstack

* check for data before removing root token

* fix root prefix and select by attributes for ie11

* use objectAt for ie11

* use blobs instead of files for ie11

* manually round cirucmference for ie11

* skip csp test on ie11

* skip tests in ie11

* include polyfill for CI

* remove on exit hooks

* update which browserstack tests are run

* remove ie check since we are not running these tests in ie

* remove ie check since we are not running these tests in ie
This commit is contained in:
Noelle Daley
2019-05-03 15:20:14 -07:00
committed by GitHub
parent 059c0c4c35
commit e9b5056a1b
19 changed files with 399 additions and 46 deletions

View File

@@ -39,6 +39,7 @@ branches:
env:
- TEST_COMMAND='make dev test-ember'
- TEST_COMMAND='make dev ember-ci-test'
- TEST_COMMAND='travis_wait 75 make testtravis'
- TEST_COMMAND='travis_wait 75 make testracetravis'
- GO111MODULE=on

View File

@@ -128,6 +128,12 @@ test-ember:
@echo "--> Running ember tests"
@cd ui && yarn run test-oss
ember-ci-test:
@echo "--> Installing JavaScript assets"
@cd ui && yarn --ignore-optional
@echo "--> Running ember tests in Browserstack"
@cd ui && yarn run test:browserstack
ember-dist:
@echo "--> Installing JavaScript assets"
@cd ui && yarn --ignore-optional

View File

@@ -7,6 +7,8 @@
- [Running / Development](#running--development)
- [Code Generators](#code-generators)
- [Running Tests](#running-tests)
- [Automated Cross-Browser Testing](#automated-cross-browser-testing)
- [Running Browserstack Locally](#running-browserstack-locally)
- [Linting](#linting)
- [Building Vault UI into a Vault Binary](#building-vault-ui-into-a-vault-binary)
- [Vault Storybook](#vault-storybook)
@@ -71,6 +73,17 @@ acceptance tests then run, proxing requests back to that server.
- `yarn run test-oss -s` to keep the test server running after the initial run.
- `yarn run test -f="policies"` to filter the tests that are run. `-f` gets passed into
[QUnit's `filter` config](https://api.qunitjs.com/config/QUnit.config#qunitconfigfilter-string--default-undefined)
- `yarn run test:browserstack` to run the kv acceptance tests in Browserstack
#### Automated Cross-Browser Testing
Vault uses [Browserstack Automate](https://automate.browserstack.com/) to run all the kv acceptance tests on various browsers. You can view the list of browsers we test by viewing `testem.browserstack.js`.
##### Running Browserstack Locally
To run the Browserstack tests locally you will need to add your `BROWSERSTACK_USERNAME` and `BROWSERSTACK_ACCESS_KEY` to your environment. Then run `yarn run test:browserstack`. You can view the currently running tests at `localhost:7357` or log in to [Browserstack Automate](https://automate.browserstack.com/) to view a previous build.
To run the tests locally in a browser other than IE11, swap out `launch_in_ci: ['BS_IE_11']` inside `testem.browserstack.js`.
### Linting
@@ -157,3 +170,4 @@ It is important to add all new components into Storybook and to keep the story a
- [Storybook for Ember Live Example](https://storybooks-ember.netlify.com/?path=/story/addon-centered--button)
- [Storybook Addons](https://github.com/storybooks/storybook/tree/master/addons/)
- [Storybook Docs](https://storybook.js.org/docs/basics/introduction/)
- [Browserstack Automate](https://automate.browserstack.com/)

View File

@@ -107,7 +107,8 @@ export default Component.extend({
if (!pem) {
return [];
}
const pemFile = new File([pem], { type: 'text/plain' });
const pemFile = new Blob([pem], { type: 'text/plain' });
const links = [
{
display: 'Download CA Certificate in PEM format',
@@ -121,7 +122,7 @@ export default Component.extend({
},
];
if (caChain) {
const caChainFile = new File([caChain], { type: 'text/plain' });
const caChainFile = new Blob([caChain], { type: 'text/plain' });
links.push({
display: 'Download CA Certificate Chain',
name: `${backend}_ca_chain.pem`,

View File

@@ -12,7 +12,7 @@ import { supportedAuthBackends } from 'vault/helpers/supported-auth-backends';
import { task, timeout } from 'ember-concurrency';
const TOKEN_SEPARATOR = '☃';
const TOKEN_PREFIX = 'vault-';
const ROOT_PREFIX = '🗝';
const ROOT_PREFIX = '_root_';
const BACKENDS = supportedAuthBackends();
export { TOKEN_SEPARATOR, TOKEN_PREFIX, ROOT_PREFIX };
@@ -296,9 +296,10 @@ export default Service.extend({
if (this.environment() === 'development') {
return;
}
this.getTokensFromStorage().forEach(key => {
const data = this.getTokenData(key);
if (data.policies.includes('root')) {
if (data && data.policies.includes('root')) {
this.removeTokenData(key);
}
});

View File

@@ -6,6 +6,7 @@ const EmberApp = require('ember-cli/lib/broccoli/ember-app');
const environment = EmberApp.env();
const isProd = environment === 'production';
const isTest = environment === 'test';
const isCI = !!process.env.CI;
module.exports = function(defaults) {
var app = new EmberApp(defaults, {
@@ -17,7 +18,7 @@ module.exports = function(defaults) {
plugins: ['transform-object-rest-spread'],
},
'ember-cli-babel': {
includePolyfill: isTest || isProd,
includePolyfill: isTest || isProd || isCI,
},
hinting: isTest,
tests: isTest,

View File

@@ -19,6 +19,7 @@
"start": "export VAULT_ADDR=http://localhost:8200; ember server --proxy=$VAULT_ADDR",
"start2": "ember server --proxy=http://localhost:8202 --port=4202",
"test": "node scripts/start-vault.js & yarn run lint:js & ember test",
"test:browserstack": "export CI=true; node scripts/start-vault.js & node scripts/run-browserstack-tests.js",
"test-oss": "yarn run test -f='!enterprise'",
"test-quick": "node scripts/start-vault.js & ember test",
"test-quick-oss": "yarn run test-quick -f='!enterprise'",
@@ -63,6 +64,7 @@
"ember-cli": "~3.5.1",
"ember-cli-autoprefixer": "^0.8.1",
"ember-cli-babel": "^6.16.0",
"ember-cli-browserstack": "^0.0.7",
"ember-cli-clipboard": "^0.8.0",
"ember-cli-content-security-policy": "^1.0.0",
"ember-cli-dependency-checker": "^3.0.0",

View File

@@ -0,0 +1,37 @@
#!/usr/bin/env node
/* eslint-env node */
/* eslint-disable no-console */
const execa = require('execa');
const chalk = require('chalk');
function run(command, args = []) {
console.log(chalk.dim('$ ' + command + ' ' + args.join(' ')));
let p = execa(command, args);
p.stdout.pipe(process.stdout);
p.stderr.pipe(process.stderr);
return p;
}
(async function() {
await run('ember', ['browserstack:connect']);
try {
try {
await run('ember', ['test', '-f=secrets/secret/create', '-c', 'testem.browserstack.js']);
console.log('success');
process.exit(0);
} finally {
if (process.env.TRAVIS_JOB_NUMBER) {
await run('ember', ['browserstack:results']);
}
await run('ember', ['browserstack:disconnect']);
}
} catch (error) {
console.log('error');
console.log(error);
process.exit(1);
}
})();

92
ui/testem.browserstack.js Normal file
View File

@@ -0,0 +1,92 @@
/* eslint-env node */
module.exports = {
framework: 'qunit',
test_page: 'tests/index.html?hidepassed&nolint',
tap_quiet_logs: true,
disable_watching: true,
timeout: 1200,
browser_start_timeout: 2000,
parallel: 4,
launchers: {
BS_Chrome_Current: {
exe: 'node_modules/.bin/browserstack-launch',
args: [
'--os',
'Windows',
'--osv',
'10',
'--b',
'chrome',
'--bv',
'latest',
'-t',
'1200',
'--u',
'<url>',
],
protocol: 'browser',
},
BS_Firefox_Current: {
exe: 'node_modules/.bin/browserstack-launch',
args: [
'--os',
'Windows',
'--osv',
'10',
'--b',
'firefox',
'--bv',
'latest',
'-t',
'1200',
'--u',
'<url>',
],
protocol: 'browser',
},
BS_Safari_Current: {
exe: 'node_modules/.bin/browserstack-launch',
args: [
'--os',
'OS X',
'--osv',
'Mojave',
'--b',
'safari',
'--bv',
'latest',
'-t',
'1200',
'--u',
'<url>',
],
protocol: 'browser',
},
BS_IE_11: {
exe: 'node_modules/.bin/browserstack-launch',
args: [
'--os',
'Windows',
'--osv',
'10',
'--b',
'ie',
'--bv',
'11.0',
'-t',
'1200',
'--u',
'<url>&legacy=true',
],
protocol: 'browser',
},
},
launch_in_dev: [],
launch_in_ci: ['BS_IE_11'],
proxies: {
'/v1': {
target: 'http://localhost:9200',
},
},
};

View File

@@ -110,7 +110,7 @@ module('Acceptance | secrets/secret/create', function(hooks) {
assert.ok(secretLink, 'link to the 3/ branch displays properly');
await listPage.secrets.filterBy('text', '3/')[0].click();
await listPage.secrets[0].menuToggle();
await listPage.secrets.objectAt(0).menuToggle();
await settled();
await listPage.delete();
await listPage.confirmDelete();
@@ -118,7 +118,7 @@ module('Acceptance | secrets/secret/create', function(hooks) {
assert.equal(currentRouteName(), 'vault.cluster.secrets.backend.list');
assert.equal(currentURL(), `/vault/secrets/${enginePath}/list/1/2/3/`, 'remains on the page');
await listPage.secrets[0].menuToggle();
await listPage.secrets.objectAt(0).menuToggle();
await listPage.delete();
await listPage.confirmDelete();
await settled();
@@ -319,7 +319,7 @@ module('Acceptance | secrets/secret/create', function(hooks) {
assert.equal(listPage.secrets.length, 3, 'renders three secrets');
await listPage.filterInput('filter/foo1');
assert.equal(listPage.secrets.length, 1, 'renders only one secret');
await listPage.secrets[0].click();
await listPage.secrets.objectAt(0).click();
await showPage.breadcrumbs.filterBy('text', 'filter')[0].click();
assert.equal(listPage.secrets.length, 3, 'renders three secrets');
assert.equal(listPage.filterInputValue, 'filter/', 'pageFilter has been reset');

View File

@@ -51,6 +51,8 @@ const OIDC_AUTH_RESPONSE = {
},
};
const WAIT_TIME = 50;
const routerStub = Service.extend({
urlFor() {
return 'http://example.com';

View File

@@ -45,7 +45,7 @@ module('Integration | Component | config pki ca', function(hooks) {
pem: pem,
backend: 'pki',
caChain: 'caChain',
der: new File(['der'], { type: 'text/plain' }),
der: new Blob(['der'], { type: 'text/plain' }),
});
};

View File

@@ -36,16 +36,19 @@ module('Integration | Component | form field', function(hooks) {
this.set('model', model);
await render(hbs`{{form-field attr=attr model=model}}`);
assert.equal(component.fields[0].labelText, 'Foo', 'renders a label');
assert.equal(component.fields.objectAt(0).labelText, 'Foo', 'renders a label');
assert.notOk(component.hasInput, 'renders only the label');
});
test('it renders: string', async function(assert) {
let [model, spy] = await setup.call(this, createAttr('foo', 'string', { defaultValue: 'default' }));
assert.equal(component.fields[0].labelText, 'Foo', 'renders a label');
assert.equal(component.fields[0].inputValue, 'default', 'renders default value');
assert.equal(component.fields.objectAt(0).labelText, 'Foo', 'renders a label');
assert.equal(component.fields.objectAt(0).inputValue, 'default', 'renders default value');
assert.ok(component.hasInput, 'renders input for string');
await component.fields[0].input('bar').change();
await component.fields
.objectAt(0)
.input('bar')
.change();
assert.equal(model.get('foo'), 'bar');
assert.ok(spy.calledWith('foo', 'bar'), 'onChange called with correct args');
@@ -53,10 +56,10 @@ module('Integration | Component | form field', function(hooks) {
test('it renders: boolean', async function(assert) {
let [model, spy] = await setup.call(this, createAttr('foo', 'boolean', { defaultValue: false }));
assert.equal(component.fields[0].labelText, 'Foo', 'renders a label');
assert.notOk(component.fields[0].inputChecked, 'renders default value');
assert.equal(component.fields.objectAt(0).labelText, 'Foo', 'renders a label');
assert.notOk(component.fields.objectAt(0).inputChecked, 'renders default value');
assert.ok(component.hasCheckbox, 'renders a checkbox for boolean');
await component.fields[0].clickLabel();
await component.fields.objectAt(0).clickLabel();
assert.equal(model.get('foo'), true);
assert.ok(spy.calledWith('foo', true), 'onChange called with correct args');
@@ -64,10 +67,13 @@ module('Integration | Component | form field', function(hooks) {
test('it renders: number', async function(assert) {
let [model, spy] = await setup.call(this, createAttr('foo', 'number', { defaultValue: 5 }));
assert.equal(component.fields[0].labelText, 'Foo', 'renders a label');
assert.equal(component.fields[0].inputValue, 5, 'renders default value');
assert.equal(component.fields.objectAt(0).labelText, 'Foo', 'renders a label');
assert.equal(component.fields.objectAt(0).inputValue, 5, 'renders default value');
assert.ok(component.hasInput, 'renders input for number');
await component.fields[0].input(8).change();
await component.fields
.objectAt(0)
.input(8)
.change();
assert.equal(model.get('foo'), 8);
assert.ok(spy.calledWith('foo', '8'), 'onChange called with correct args');
@@ -75,7 +81,7 @@ module('Integration | Component | form field', function(hooks) {
test('it renders: object', async function(assert) {
await setup.call(this, createAttr('foo', 'object'));
assert.equal(component.fields[0].labelText, 'Foo', 'renders a label');
assert.equal(component.fields.objectAt(0).labelText, 'Foo', 'renders a label');
assert.ok(component.hasJSONEditor, 'renders the json editor');
});
@@ -84,10 +90,10 @@ module('Integration | Component | form field', function(hooks) {
this,
createAttr('foo', 'string', { defaultValue: 'goodbye', editType: 'textarea' })
);
assert.equal(component.fields[0].labelText, 'Foo', 'renders a label');
assert.equal(component.fields.objectAt(0).labelText, 'Foo', 'renders a label');
assert.ok(component.hasTextarea, 'renders a textarea');
assert.equal(component.fields[0].textareaValue, 'goodbye', 'renders default value');
await component.fields[0].textarea('hello');
assert.equal(component.fields.objectAt(0).textareaValue, 'goodbye', 'renders default value');
await component.fields.objectAt(0).textarea('hello');
assert.equal(model.get('foo'), 'hello');
assert.ok(spy.calledWith('foo', 'hello'), 'onChange called with correct args');
@@ -101,8 +107,11 @@ module('Integration | Component | form field', function(hooks) {
test('it renders: editType ttl', async function(assert) {
let [model, spy] = await setup.call(this, createAttr('foo', null, { editType: 'ttl' }));
assert.ok(component.hasTTLPicker, 'renders the ttl-picker component');
await component.fields[0].input('3');
await component.fields[0].select('h').change();
await component.fields.objectAt(0).input('3');
await component.fields
.objectAt(0)
.select('h')
.change();
assert.equal(model.get('foo'), '3h');
assert.ok(spy.calledWith('foo', '3h'), 'onChange called with correct args');
@@ -112,7 +121,10 @@ module('Integration | Component | form field', function(hooks) {
let [model, spy] = await setup.call(this, createAttr('foo', 'string', { editType: 'stringArray' }));
assert.ok(component.hasStringList, 'renders the string-list component');
await component.fields[0].input('array').change();
await component.fields
.objectAt(0)
.input('array')
.change();
assert.deepEqual(model.get('foo'), ['array'], 'sets the value on the model');
assert.deepEqual(spy.args[0], ['foo', ['array']], 'onChange called with correct args');
});
@@ -120,14 +132,14 @@ module('Integration | Component | form field', function(hooks) {
test('it renders: sensitive', async function(assert) {
let [model, spy] = await setup.call(this, createAttr('password', 'string', { sensitive: true }));
assert.ok(component.hasMaskedInput, 'renders the masked-input component');
await component.fields[0].textarea('secret');
await component.fields.objectAt(0).textarea('secret');
assert.equal(model.get('password'), 'secret');
assert.ok(spy.calledWith('password', 'secret'), 'onChange called with correct args');
});
test('it uses a passed label', async function(assert) {
await setup.call(this, createAttr('foo', 'string', { label: 'Not Foo' }));
assert.equal(component.fields[0].labelText, 'Not Foo', 'renders the label from options');
assert.equal(component.fields.objectAt(0).labelText, 'Not Foo', 'renders the label from options');
});
test('it renders a help tooltip', async function(assert) {

View File

@@ -30,15 +30,19 @@ module('Integration | Component | license info', function(hooks) {
assert.equal(component.hasSaveButton, true, 'it renders the save button');
assert.equal(component.hasTextInput, true, 'it renders text input for new license');
assert.equal(component.featureRows.length, FEATURES.length, 'it renders all of the features');
assert.equal(component.featureRows[0].featureName, 'HSM', 'it renders HSM feature');
assert.equal(component.featureRows[0].featureStatus, 'Active', 'it renders Active for HSM feature');
assert.equal(component.featureRows.objectAt(0).featureName, 'HSM', 'it renders HSM feature');
assert.equal(
component.featureRows[1].featureName,
component.featureRows.objectAt(0).featureStatus,
'Active',
'it renders Active for HSM feature'
);
assert.equal(
component.featureRows.objectAt(1).featureName,
'Performance Replication',
'it renders Performance Replication feature name'
);
assert.equal(
component.featureRows[1].featureStatus,
component.featureRows.objectAt(1).featureStatus,
'Not Active',
'it renders Not Active for Performance Replication'
);

View File

@@ -6,7 +6,8 @@ import hbs from 'htmlbars-inline-precompile';
let file;
const fileEvent = () => {
const data = { some: 'content' };
file = new File([JSON.stringify(data, null, 2)], 'file.json', { type: 'application/json' });
file = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
file.name = 'file.json';
return ['change', { files: [file] }];
};

View File

@@ -19,7 +19,9 @@ module('Integration | Component | radial progress', function(hooks) {
});
test('it renders', async function(assert) {
let circumference = (19 / 2) * Math.PI * 2;
// We have to manually round the circumference, strokeDash, and strokeDashOffset because
// ie11 truncates decimals differently than other browsers.
let circumference = ((19 / 2) * Math.PI * 2).toFixed(2);
await render(hbs`{{radial-progress progressDecimal=0.5}}`);
assert.equal(component.viewBox, '0 0 20 20');
@@ -29,7 +31,7 @@ module('Integration | Component | radial progress', function(hooks) {
assert.equal(component.r, 19 / 2);
assert.equal(component.cx, 10);
assert.equal(component.cy, 10);
assert.equal(component.strokeDash, circumference);
assert.equal(component.strokeDashOffset, circumference * 0.5);
assert.equal(Number(component.strokeDash).toFixed(2), circumference);
assert.equal(Number(component.strokeDashOffset).toFixed(3), (circumference * 0.5).toFixed(3));
});
});

View File

@@ -67,7 +67,11 @@ module('Integration | Component | search select', function(hooks) {
await render(hbs`{{search-select label="foo" models=models onChange=onChange}}`);
await clickTrigger();
assert.equal(component.options.length, 3, 'shows all options');
assert.equal(component.options[0].text, component.selectedOptionText, 'first object in list is focused');
assert.equal(
component.options.objectAt(0).text,
component.selectedOptionText,
'first object in list is focused'
);
});
test('it filters options when text is entered', async function(assert) {
@@ -114,7 +118,7 @@ module('Integration | Component | search select', function(hooks) {
this.set('inputValue', ['8']);
await render(hbs`{{search-select label="foo" inputValue=inputValue models=models onChange=onChange}}`);
assert.equal(component.selectedOptions.length, 1, 'there is 1 selected option');
await component.deleteButtons[0].click();
await component.deleteButtons.objectAt(0).click();
assert.equal(component.selectedOptions.length, 0, 'there are no selected options');
assert.ok(this.onChange.calledOnce);
assert.ok(this.onChange.calledWith([]));
@@ -141,7 +145,11 @@ module('Integration | Component | search select', function(hooks) {
);
await clickTrigger();
assert.equal(component.options.length, 1, 'has the disabled no results option');
assert.equal(component.options[0].text, 'No results found', 'text of option shows No results found');
assert.equal(
component.options.objectAt(0).text,
'No results found',
'text of option shows No results found'
);
});
test('it shows both name and smaller id for identity endpoints', async function(assert) {

View File

@@ -1,4 +1,4 @@
import { clickable, collection, fillable, text, value } from 'ember-cli-page-object';
import { clickable, collection, fillable, text, value, attribute } from 'ember-cli-page-object';
import fields from './form-field';
import errorText from './alert-banner';
@@ -14,11 +14,11 @@ export default {
pathValue: value('[data-test-input="path"]'),
types: collection('[data-test-mount-type-radio] input', {
select: clickable(),
mountType: value(),
id: attribute('id'),
}),
type: fillable('[name="mount-type"]'),
async selectType(type) {
return this.types.filterBy('mountType', type)[0].select();
return this.types.filterBy('id', type)[0].select();
},
async mount(type, path) {
await this.selectType(type);

View File

@@ -4834,6 +4834,24 @@ browserslist@^4.3.4, browserslist@^4.4.2:
electron-to-chromium "^1.3.113"
node-releases "^1.1.8"
browserstack-local@^1.3.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/browserstack-local/-/browserstack-local-1.4.0.tgz#d979cac056f57b9af159b3bcd7fdc09b4354537c"
integrity sha512-BUJWxIsJkJxqfTPJIvGWTsf+IYSqSFUeFNW9tnuyTG7va/0LkXLhIi/ErFGDle1urQkol48HlQUXj4QrliXFpg==
dependencies:
https-proxy-agent "^2.2.1"
is-running "^2.0.0"
ps-tree "=1.1.1"
sinon "^1.17.6"
temp-fs "^0.9.9"
browserstack@^1.5.0:
version "1.5.2"
resolved "https://registry.yarnpkg.com/browserstack/-/browserstack-1.5.2.tgz#17d8bb76127a1cc0ea416424df80d218f803673f"
integrity sha512-+6AFt9HzhKykcPF79W6yjEUJcdvZOV0lIXdkORXMJftGrDl0OKWqRF4GHqpDNkxiceDT/uB7Fb/aDwktvXX7dg==
dependencies:
https-proxy-agent "^2.2.1"
bser@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/bser/-/bser-2.0.0.tgz#9ac78d3ed5d915804fd87acb158bc797147a1719"
@@ -6710,7 +6728,7 @@ duplexer3@^0.1.4:
resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2"
integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=
duplexer@^0.1.1:
duplexer@^0.1.1, duplexer@~0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1"
integrity sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=
@@ -7037,6 +7055,16 @@ ember-cli-broccoli-sane-watcher@^2.1.1:
rsvp "^3.0.18"
sane "^2.4.1"
ember-cli-browserstack@^0.0.7:
version "0.0.7"
resolved "https://registry.yarnpkg.com/ember-cli-browserstack/-/ember-cli-browserstack-0.0.7.tgz#79327eff1d2330f8baadada440413d5c7af22862"
integrity sha512-7uWk0hdqCVKpjT2CG5CAKl/NCnL3bE1ITMoNGixx2QNzRUfq4TWQ+1geLpvObzmMLTY0PWlG1Rx152fXRxtTAw==
dependencies:
browserstack "^1.5.0"
browserstack-local "^1.3.0"
rsvp "^4.7.0"
yargs "^10.0.3"
ember-cli-clipboard@^0.8.0:
version "0.8.1"
resolved "https://registry.yarnpkg.com/ember-cli-clipboard/-/ember-cli-clipboard-0.8.1.tgz#59f8eb6ba471a7668dff592fcebb7b06014240dd"
@@ -8210,6 +8238,19 @@ etag@~1.8.1:
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
event-stream@=3.3.4:
version "3.3.4"
resolved "https://registry.yarnpkg.com/event-stream/-/event-stream-3.3.4.tgz#4ab4c9a0f5a54db9338b4c34d86bfce8f4b35571"
integrity sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=
dependencies:
duplexer "~0.1.1"
from "~0"
map-stream "~0.1.0"
pause-stream "0.0.11"
split "0.3"
stream-combiner "~0.0.4"
through "~2.3.1"
eventemitter3@^3.0.0, eventemitter3@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.0.tgz#090b4d6cdbd645ed10bf750d4b5407942d7ba163"
@@ -8951,6 +8992,13 @@ format@^0.2.2:
resolved "https://registry.yarnpkg.com/format/-/format-0.2.2.tgz#d6170107e9efdc4ed30c9dc39016df942b5cb58b"
integrity sha1-1hcBB+nv3E7TDJ3DkBbflCtctYs=
formatio@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/formatio/-/formatio-1.1.1.tgz#5ed3ccd636551097383465d996199100e86161e9"
integrity sha1-XtPM1jZVEJc4NGXZlhmRAOhhYek=
dependencies:
samsam "~1.1"
formatio@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/formatio/-/formatio-1.2.0.tgz#f3b2167d9068c4698a8d51f4f760a39a54d818eb"
@@ -8991,6 +9039,11 @@ from2@^2.1.0, from2@^2.1.1:
inherits "^2.0.1"
readable-stream "^2.0.0"
from@~0:
version "0.1.7"
resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe"
integrity sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=
fs-extra@^0.24.0:
version "0.24.0"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.24.0.tgz#d4e4342a96675cb7846633a6099249332b539952"
@@ -10326,6 +10379,11 @@ is-alphanumerical@^1.0.0:
is-alphabetical "^1.0.0"
is-decimal "^1.0.0"
is-arguments@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.0.4.tgz#3faf966c7cba0ff437fb31f6250082fcf0448cf3"
integrity sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==
is-arrayish@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
@@ -10496,6 +10554,11 @@ is-function@^1.0.1:
resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.1.tgz#12cfb98b65b57dd3d193a3121f5f6e2f437602b5"
integrity sha1-Es+5i2W1fdPRk6MSH19uL0N2ArU=
is-generator-function@^1.0.7:
version "1.0.7"
resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.7.tgz#d2132e529bb0000a7f80794d4bdf5cd5e5813522"
integrity sha512-YZc5EwyO4f2kWCax7oegfuSr9mFz1ZvieNYBEjmukLxgXfBUbxAWGVF7GZf0zidYtoBl3WvC07YK0wT76a+Rtw==
is-git-url@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-git-url/-/is-git-url-1.0.0.tgz#53f684cd143285b52c3244b4e6f28253527af66b"
@@ -10656,6 +10719,11 @@ is-root@2.0.0:
resolved "https://registry.yarnpkg.com/is-root/-/is-root-2.0.0.tgz#838d1e82318144e5a6f77819d90207645acc7019"
integrity sha512-F/pJIk8QD6OX5DNhRB7hWamLsUilmkDGho48KbgZ6xg/lmAZXHxzXQ91jzB3yRSw5kdQGGGc4yz8HYhTYIMWPg==
is-running@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/is-running/-/is-running-2.1.0.tgz#30a73ff5cc3854e4fc25490809e9f5abf8de09e0"
integrity sha1-MKc/9cw4VOT8JUkICen1q/jeCeA=
is-ssh@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/is-ssh/-/is-ssh-1.3.0.tgz#ebea1169a2614da392a63740366c3ce049d8dff6"
@@ -11999,6 +12067,11 @@ loglevel@^1.4.1:
resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.1.tgz#e0fc95133b6ef276cdc8887cdaf24aa6f156f8fa"
integrity sha1-4PyVEztu8nbNyIh82vJKpvFW+Po=
lolex@1.3.2:
version "1.3.2"
resolved "https://registry.yarnpkg.com/lolex/-/lolex-1.3.2.tgz#7c3da62ffcb30f0f5a80a2566ca24e45d8a01f31"
integrity sha1-fD2mL/yzDw9agKJWbKJORdigHzE=
lolex@^2.1.2, lolex@^2.3.2:
version "2.7.4"
resolved "https://registry.yarnpkg.com/lolex/-/lolex-2.7.4.tgz#6514de2c3291e9d6f09d49ddce4a95f7d4d5a93f"
@@ -12154,6 +12227,11 @@ map-or-similar@^1.5.0:
resolved "https://registry.yarnpkg.com/map-or-similar/-/map-or-similar-1.5.0.tgz#6de2653174adfb5d9edc33c69d3e92a1b76faf08"
integrity sha1-beJlMXSt+12e3DPGnT6Sobdvrwg=
map-stream@~0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194"
integrity sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=
map-visit@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f"
@@ -13384,7 +13462,7 @@ object-visit@^1.0.0:
dependencies:
isobject "^3.0.0"
object.entries@^1.0.4:
object.entries@^1.0.4, object.entries@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.0.tgz#2024fc6d6ba246aee38bdb0ffd5cfbcf371b7519"
integrity sha512-l+H6EQ8qzGRxbkHOd5I/aHRhHDKoQXQ8g0BYt4uSweQU1/J6dZUOyWh9a2Vky35YCKjzmgxOzta2hH6kf9HuXA==
@@ -13926,6 +14004,13 @@ path-type@^3.0.0:
dependencies:
pify "^3.0.0"
pause-stream@0.0.11:
version "0.0.11"
resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445"
integrity sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=
dependencies:
through "~2.3"
pbkdf2@^3.0.3:
version "3.0.16"
resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.16.tgz#7404208ec6b01b62d85bf83853a8064f8d9c2a5c"
@@ -14387,6 +14472,13 @@ prr@~1.0.1:
resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476"
integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY=
ps-tree@=1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/ps-tree/-/ps-tree-1.1.1.tgz#5f1ba35455b8c25eeb718d04c37de1555d96d3db"
integrity sha512-kef7fYYSKVqQffmzTMsVcUD1ObNJMp8sNSmHGlGKsZQyL/ht9MZKk86u0Rd1NhpTOAuhqwKCLLpktwkqz+MF8A==
dependencies:
event-stream "=3.3.4"
pseudomap@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
@@ -15557,6 +15649,13 @@ rimraf@~2.2.6:
resolved "http://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz#e439be2aaee327321952730f99a8929e4fc50582"
integrity sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=
rimraf@~2.5.2:
version "2.5.4"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.5.4.tgz#96800093cbf1a0c86bd95b4625467535c29dfa04"
integrity sha1-loAAk8vxoMhr2VtGJUZ1NcKd+gQ=
dependencies:
glob "^7.0.5"
ripemd160@^2.0.0, ripemd160@^2.0.1:
version "2.0.2"
resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c"
@@ -15704,11 +15803,21 @@ safe-regex@^1.1.0:
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
samsam@1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/samsam/-/samsam-1.1.2.tgz#bec11fdc83a9fda063401210e40176c3024d1567"
integrity sha1-vsEf3IOp/aBjQBIQ5AF2wwJNFWc=
samsam@1.3.0, samsam@1.x, samsam@^1.1.3:
version "1.3.0"
resolved "https://registry.yarnpkg.com/samsam/-/samsam-1.3.0.tgz#8d1d9350e25622da30de3e44ba692b5221ab7c50"
integrity sha512-1HwIYD/8UlOtFS3QO3w7ey+SdSDFE4HRNLZoZRYVQefrOY3l17epswImeB1ijgJFQJodIaHcwkp3r/myBjFVbg==
samsam@~1.1:
version "1.1.3"
resolved "https://registry.yarnpkg.com/samsam/-/samsam-1.1.3.tgz#9f5087419b4d091f232571e7fa52e90b0f552621"
integrity sha1-n1CHQZtNCR8jJXHn+lLpCw9VJiE=
sane@^2.4.1:
version "2.5.2"
resolved "https://registry.yarnpkg.com/sane/-/sane-2.5.2.tgz#b4dc1861c21b427e929507a3e751e2a2cb8ab3fa"
@@ -16060,6 +16169,16 @@ simple-swizzle@^0.2.2:
dependencies:
is-arrayish "^0.3.1"
sinon@^1.17.6:
version "1.17.7"
resolved "https://registry.yarnpkg.com/sinon/-/sinon-1.17.7.tgz#4542a4f49ba0c45c05eb2e9dd9d203e2b8efe0bf"
integrity sha1-RUKk9JugxFwF6y6d2dID4rjv4L8=
dependencies:
formatio "1.1.1"
lolex "1.3.2"
samsam "1.1.2"
util ">=0.10.3 <1"
sinon@^3.2.1:
version "3.3.0"
resolved "https://registry.yarnpkg.com/sinon/-/sinon-3.3.0.tgz#9132111b4bbe13c749c2848210864250165069b1"
@@ -16457,6 +16576,13 @@ split2@~1.0.0:
dependencies:
through2 "~2.0.0"
split@0.3:
version "0.3.3"
resolved "https://registry.yarnpkg.com/split/-/split-0.3.3.tgz#cd0eea5e63a211dfff7eb0f091c4133e2d0dd28f"
integrity sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=
dependencies:
through "2"
split@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/split/-/split-1.0.1.tgz#605bd9be303aa59fb35f9229fbea0ddec9ea07d9"
@@ -16570,6 +16696,13 @@ stream-combiner2@~1.1.1:
duplexer2 "~0.1.0"
readable-stream "^2.0.2"
stream-combiner@~0.0.4:
version "0.0.4"
resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.0.4.tgz#4d5e433c185261dde623ca3f44c586bcf5c4ad14"
integrity sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=
dependencies:
duplexer "~0.1.1"
stream-connect@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/stream-connect/-/stream-connect-1.0.2.tgz#18bc81f2edb35b8b5d9a8009200a985314428a97"
@@ -16980,6 +17113,13 @@ telejson@^2.1.0, telejson@^2.1.1:
memoizerific "^1.11.3"
safe-eval "^0.4.1"
temp-fs@^0.9.9:
version "0.9.9"
resolved "https://registry.yarnpkg.com/temp-fs/-/temp-fs-0.9.9.tgz#8071730437870720e9431532fe2814364f8803d7"
integrity sha1-gHFzBDeHByDpQxUy/igUNk+IA9c=
dependencies:
rimraf "~2.5.2"
temp-path@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/temp-path/-/temp-path-1.0.0.tgz#24b1543973ab442896d9ad367dd9cbdbfafe918b"
@@ -17127,7 +17267,7 @@ through2@^2.0.0, through2@^2.0.2, through2@~2.0.0:
readable-stream "^2.1.5"
xtend "~4.0.1"
through@2, "through@>=2.2.7 <3", through@^2.3.6, through@^2.3.8:
through@2, "through@>=2.2.7 <3", through@^2.3.6, through@^2.3.8, through@~2.3, through@~2.3.1:
version "2.3.8"
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=
@@ -17784,6 +17924,17 @@ util@0.10.3:
dependencies:
inherits "2.0.1"
"util@>=0.10.3 <1":
version "0.12.0"
resolved "https://registry.yarnpkg.com/util/-/util-0.12.0.tgz#bb5e3d29ba2703c7add0ad337003be3ca477798a"
integrity sha512-pPSOFl7VLhZ7LO/SFABPraZEEurkJUWSMn3MuA/r3WQZc+Z1fqou2JqLSOZbCLl73EUIxuUVX8X4jkX2vfJeAA==
dependencies:
inherits "2.0.3"
is-arguments "^1.0.4"
is-generator-function "^1.0.7"
object.entries "^1.1.0"
safe-buffer "^5.1.2"
util@^0.10.3:
version "0.10.4"
resolved "https://registry.yarnpkg.com/util/-/util-0.10.4.tgz#3aa0125bfe668a4672de58857d3ace27ecb76901"
@@ -18335,7 +18486,7 @@ yargs-parser@^5.0.0:
dependencies:
camelcase "^3.0.0"
yargs-parser@^8.0.0:
yargs-parser@^8.0.0, yargs-parser@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-8.1.0.tgz#f1376a33b6629a5d063782944da732631e966950"
integrity sha512-yP+6QqN8BmrgW2ggLtTbdrOyBNSI7zBa4IykmiV5R1wl1JWNxQvWhMfMdmzIYtKU7oP3OOInY/tl2ov3BDjnJQ==
@@ -18367,6 +18518,24 @@ yargs@10.0.3:
y18n "^3.2.1"
yargs-parser "^8.0.0"
yargs@^10.0.3:
version "10.1.2"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-10.1.2.tgz#454d074c2b16a51a43e2fb7807e4f9de69ccb5c5"
integrity sha512-ivSoxqBGYOqQVruxD35+EyCFDYNEFL/Uo6FcOnz+9xZdZzK0Zzw4r4KhbrME1Oo2gOggwJod2MnsdamSG7H9ig==
dependencies:
cliui "^4.0.0"
decamelize "^1.1.1"
find-up "^2.1.0"
get-caller-file "^1.0.1"
os-locale "^2.0.0"
require-directory "^2.1.1"
require-main-filename "^1.0.1"
set-blocking "^2.0.0"
string-width "^2.0.0"
which-module "^2.0.0"
y18n "^3.2.1"
yargs-parser "^8.1.0"
yargs@^11.0.0:
version "11.1.0"
resolved "http://registry.npmjs.org/yargs/-/yargs-11.1.0.tgz#90b869934ed6e871115ea2ff58b03f4724ed2d77"