Commit 5670194d authored by Robert Knight's avatar Robert Knight

Merge branch 'master' into group-list-item-oos-react

parents b36b7a3d 14b2bd17
......@@ -9,6 +9,9 @@
}
}]
],
"plugins": ["angularjs-annotate"],
"plugins": [
"angularjs-annotate",
"transform-async-to-promises"
],
"ignore": ["**/vendor/*"]
}
......@@ -9,7 +9,9 @@
"rules": {
"mocha/no-exclusive-tests": "error",
"no-var": "error",
"indent": "off"
"indent": "off",
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "error"
},
"parserOptions": {
"ecmaVersion": 2018,
......@@ -19,7 +21,8 @@
},
"plugins": [
"mocha",
"react"
"react",
"react-hooks"
],
"settings": {
"react": {
......
......@@ -20,6 +20,7 @@
"aws-sdk": "^2.345.0",
"babel-plugin-angularjs-annotate": "^0.10.0",
"babel-plugin-istanbul": "^5.1.0",
"babel-plugin-transform-async-to-promises": "^0.8.6",
"babel-preset-env": "^1.7.0",
"babelify": "^10.0.0",
"browserify": "^16.2.3",
......@@ -53,6 +54,7 @@
"eslint-config-hypothesis": "^1.0.0",
"eslint-plugin-mocha": "^5.2.1",
"eslint-plugin-react": "^7.12.4",
"eslint-plugin-react-hooks": "^1.6.0",
"exorcist": "^1.0.1",
"express": "^4.14.1",
"extend": "^3.0.2",
......
......@@ -4,6 +4,7 @@
},
"rules": {
"no-console": "off",
"react-hooks/rules-of-hooks": "off"
},
"parserOptions": {
"ecmaVersion": 2018
......
'use strict';
const {
orgName,
trackViewGroupActivity,
} = require('../util/group-list-item-common');
// @ngInject
function GroupListItemController(analytics, store) {
this.focusGroup = function() {
analytics.track(analytics.events.GROUP_SWITCH);
store.focusGroup(this.group.id);
};
const classnames = require('classnames');
const propTypes = require('prop-types');
const { createElement } = require('preact');
this.isSelected = function() {
return this.group.id === store.focusedGroupId();
};
const { orgName } = require('../util/group-list-item-common');
this.orgName = function() {
return orgName(this.group);
function GroupListItem({ analytics, group, store }) {
const focusGroup = () => {
analytics.track(analytics.events.GROUP_SWITCH);
store.focusGroup(group.id);
};
this.trackViewGroupActivity = function() {
trackViewGroupActivity(analytics);
};
const isSelected = group.id === store.focusedGroupId();
const groupOrgName = orgName(group);
return (
<div
className={classnames({
'group-list-item__item': true,
'is-selected': isSelected,
})}
onClick={focusGroup}
tabIndex="0"
>
{/* the group icon */}
<div className="group-list-item__icon-container">
{group.logo && (
<img
className="group-list-item__icon group-list-item__icon--organization"
alt={groupOrgName}
src={group.logo}
/>
)}
</div>
{/* the group name */}
<div className="group-list-item__details">
<a
className="group-list-item__name-link"
href=""
title={
group.type === 'private'
? `Show and create annotations in ${group.name}`
: 'Show public annotations'
}
>
{group.name}
</a>
</div>
</div>
);
}
module.exports = {
controller: GroupListItemController,
controllerAs: 'vm',
bindings: {
group: '<',
},
template: require('../templates/group-list-item.html'),
GroupListItem.propTypes = {
group: propTypes.object.isRequired,
analytics: propTypes.object.isRequired,
store: propTypes.object.isRequired,
};
GroupListItem.injectedProps = ['analytics', 'store'];
module.exports = GroupListItem;
'use strict';
const angular = require('angular');
const { createElement } = require('preact');
const { mount } = require('enzyme');
const proxyquire = require('proxyquire');
const util = require('../../directive/test/util');
const { events } = require('../../services/analytics');
describe('groupListItem', () => {
describe('GroupListItem', () => {
let fakeAnalytics;
let fakeStore;
let fakeGroupListItemCommon;
let GroupListItem;
before(() => {
fakeGroupListItemCommon = {
......@@ -17,13 +18,10 @@ describe('groupListItem', () => {
trackViewGroupActivity: sinon.stub(),
};
// Return groupListItem with groupListItemCommon stubbed out.
const groupListItem = proxyquire('../group-list-item', {
GroupListItem = proxyquire('../group-list-item', {
'../util/group-list-item-common': fakeGroupListItemCommon,
'@noCallThru': true,
});
angular.module('app', []).component('groupListItem', groupListItem);
});
beforeEach(() => {
......@@ -36,64 +34,54 @@ describe('groupListItem', () => {
track: sinon.stub(),
events,
};
angular.mock.module('app', {
analytics: fakeAnalytics,
store: fakeStore,
});
});
const createGroupListItem = fakeGroup => {
return util.createDirective(document, 'groupListItem', {
group: fakeGroup,
});
return mount(
<GroupListItem
group={fakeGroup}
analytics={fakeAnalytics}
store={fakeStore}
/>
);
};
it('changes the focused group when group is clicked', () => {
const fakeGroup = { id: 'groupid' };
const element = createGroupListItem(fakeGroup);
const group = element.find('.group-list-item__item');
group[0].click();
const wrapper = createGroupListItem(fakeGroup);
wrapper.find('.group-list-item__item').simulate('click');
assert.calledWith(fakeStore.focusGroup, fakeGroup.id);
assert.calledWith(fakeAnalytics.track, fakeAnalytics.events.GROUP_SWITCH);
});
it('calls groupListItemCommon.trackViewGroupActivity when trackViewGroupActivity is called', () => {
const fakeGroup = { id: 'groupid' };
const element = createGroupListItem(fakeGroup);
element.ctrl.trackViewGroupActivity();
assert.calledWith(
fakeGroupListItemCommon.trackViewGroupActivity,
fakeAnalytics
);
});
it('returns groupListItemCommon.orgName when orgName is called', () => {
const fakeGroup = { id: 'groupid', organization: { name: 'org' } };
it('sets alt text for organization logo', () => {
const fakeGroup = {
id: 'groupid',
// Dummy scheme to avoid actually trying to load image.
logo: 'dummy://hypothes.is/logo.svg',
organization: { name: 'org' },
};
fakeGroupListItemCommon.orgName
.withArgs(fakeGroup)
.returns(fakeGroup.organization.name);
const element = createGroupListItem(fakeGroup);
const orgName = element.ctrl.orgName();
const wrapper = createGroupListItem(fakeGroup);
const altText = wrapper.find('img').props().alt;
assert.equal(orgName, fakeGroup.organization.name);
assert.equal(altText, fakeGroup.organization.name);
});
describe('isSelected', () => {
describe('selected state', () => {
[
{
description: 'returns true if group is the focused group',
description: 'is selected if group is the focused group',
focusedGroupId: 'groupid',
expectedIsSelected: true,
},
{
description: 'returns false if group is not the focused group',
description: 'is not selected if group is not the focused group',
focusedGroupId: 'other',
expectedIsSelected: false,
},
......@@ -102,9 +90,12 @@ describe('groupListItem', () => {
fakeStore.focusedGroupId.returns(focusedGroupId);
const fakeGroup = { id: 'groupid' };
const element = createGroupListItem(fakeGroup);
const wrapper = createGroupListItem(fakeGroup);
assert.equal(element.ctrl.isSelected(), expectedIsSelected);
assert.equal(
wrapper.find('.group-list-item__item').hasClass('is-selected'),
expectedIsSelected
);
});
});
});
......
......@@ -30,6 +30,7 @@ document.body.setAttribute('ng-csp', '');
disableOpenerForExternalLinks(document.body);
const angular = require('angular');
const wrapReactComponent = require('./util/wrap-react-component');
// autofill-event relies on the existence of window.angular so
// it must be require'd after angular is first require'd
......@@ -156,7 +157,10 @@ function startAngularApp(config) {
.component('dropdownMenuBtn', require('./components/dropdown-menu-btn'))
.component('excerpt', require('./components/excerpt'))
.component('groupList', require('./components/group-list'))
.component('groupListItem', require('./components/group-list-item'))
.component(
'groupListItem',
wrapReactComponent(require('./components/group-list-item'))
)
.component(
'groupListItemOutOfScope',
wrapReactComponent(require('./components/group-list-item-out-of-scope'))
......
......@@ -280,15 +280,14 @@ function auth(
*
* This revokes and then forgets any OAuth credentials that the user has.
*/
function logout() {
return Promise.all([tokenInfoPromise, oauthClient()])
.then(([token, client]) => {
return client.revokeToken(token.accessToken);
})
.then(() => {
async function logout() {
const [token, client] = await Promise.all([
tokenInfoPromise,
oauthClient(),
]);
await client.revokeToken(token.accessToken);
tokenInfoPromise = Promise.resolve(null);
localStorage.removeItem(storageKey());
});
}
listenForTokenStorageEvents();
......
......@@ -21,7 +21,6 @@ const CACHE_TTL = 5 * 60 * 1000; // 5 minutes
* @ngInject
*/
function session(
$q,
$rootScope,
analytics,
store,
......@@ -148,7 +147,7 @@ function session(
.catch(function(err) {
flash.error('Log out failed');
analytics.track(analytics.events.LOGOUT_FAILURE);
return $q.reject(new Error(err));
throw new Error(err);
})
.then(function() {
analytics.track(analytics.events.LOGOUT_SUCCESS);
......
<div
ng-class="{'group-list-item__item': true, 'is-selected': vm.isSelected()}"
ng-click="vm.focusGroup()"
tabindex="0"
>
<!-- the group icon !-->
<div class="group-list-item__icon-container">
<img
class="group-list-item__icon group-list-item__icon--organization"
alt="{{ vm.orgName() }}"
ng-src="{{ vm.group.logo }}"
ng-if="vm.group.logo"
/>
</div>
<!-- the group name and share link -->
<div class="group-list-item__details">
<a
class="group-list-item__name-link"
href=""
title="{{ vm.group.type === 'private' ? 'Show and create annotations in ' + vm.group.name : 'Show public annotations' }}"
>
{{vm.group.name}}
</a>
</div>
</div>
......@@ -720,9 +720,9 @@
universal-user-agent "^2.0.1"
"@octokit/rest@^16.9.0":
version "16.20.0"
resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-16.20.0.tgz#54462b6e540b5d40063850d370ce8e084cf127d6"
integrity sha512-tN5j64P6QymlMzKo94DG1LRNHCwMnLg5poZlVhsCfkHhEWKpofZ1qBDr2/0w6qDLav4EA1XXMmZdNpvGhc9BDQ==
version "16.22.0"
resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-16.22.0.tgz#461770ae6f8b5a50a85f7d1b2752645c3e01ba6a"
integrity sha512-1PGUwDxLuG7AZsiKaIdxJr918jcZ1FtPX0kGqxoUlssn+fIzzlwQY623gnM8vY9c9jfASD+QUQmflHh3FwBthw==
dependencies:
"@octokit/request" "2.4.2"
before-after-hook "^1.4.0"
......@@ -736,10 +736,10 @@
universal-user-agent "^2.0.0"
url-template "^2.0.8"
"@sinonjs/commons@^1", "@sinonjs/commons@^1.0.2", "@sinonjs/commons@^1.3.1":
version "1.3.1"
resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.3.1.tgz#ba4ae5fa908f7d8b96b0ec8df0665aca2d8695a4"
integrity sha512-rgmZk5CrBGAMATk0HlHOFvo8V44/r+On6cKS80tqid0Eljd+fFBWBOXZp9H2/EB3faxdNdzXTx6QZIKLkbJ7mA==
"@sinonjs/commons@^1", "@sinonjs/commons@^1.0.2", "@sinonjs/commons@^1.4.0":
version "1.4.0"
resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.4.0.tgz#7b3ec2d96af481d7a0321252e7b1c94724ec5a78"
integrity sha512-9jHK3YF/8HtJ9wCAbG+j8cD0i0+ATS9A7gXFqS36TblLPNy6rEEc+SB0imo91eCboGaBYGV/MT1/br/J+EE7Tw==
dependencies:
type-detect "4.0.8"
......@@ -751,10 +751,10 @@
"@sinonjs/commons" "^1"
"@sinonjs/samsam" "^3.1.0"
"@sinonjs/samsam@^3.1.0", "@sinonjs/samsam@^3.2.0":
version "3.2.0"
resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-3.2.0.tgz#58c62b5f1f42e46d039d073d0ae2753da676bf0c"
integrity sha512-j5F1rScewLtx6pbTK0UAjA3jJj4RYiSKOix53YWv+Jzy/AZ69qHxUpU8fwVLjyKbEEud9QrLpv6Ggs7WqTimYw==
"@sinonjs/samsam@^3.1.0", "@sinonjs/samsam@^3.3.1":
version "3.3.1"
resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-3.3.1.tgz#e88c53fbd9d91ad9f0f2b0140c16c7c107fe0d07"
integrity sha512-wRSfmyd81swH0hA1bxJZJ57xr22kC07a1N4zuIL47yTS04bDk6AoCkczcqHEjcRPmJ+FruGJ9WBQiJwMtIElFw==
dependencies:
"@sinonjs/commons" "^1.0.2"
array-from "^2.1.1"
......@@ -1089,6 +1089,11 @@ array-each@^1.0.0, array-each@^1.0.1:
resolved "https://registry.yarnpkg.com/array-each/-/array-each-1.0.1.tgz#a794af0c05ab1752846ee753a1f211a05ba0c44f"
integrity sha1-p5SvDAWrF1KEbudTofIRoFugxE8=
array-filter@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-1.0.0.tgz#baf79e62e6ef4c2a4c0b831232daffec251f9d83"
integrity sha1-uveeYubvTCpMC4MSMtr/7CUfnYM=
array-filter@~0.0.0:
version "0.0.1"
resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-0.0.1.tgz#7da8cf2e26628ed732803581fd21f67cacd2eeec"
......@@ -1533,6 +1538,11 @@ babel-plugin-transform-async-to-generator@^6.22.0:
babel-plugin-syntax-async-functions "^6.8.0"
babel-runtime "^6.22.0"
babel-plugin-transform-async-to-promises@^0.8.6:
version "0.8.8"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-async-to-promises/-/babel-plugin-transform-async-to-promises-0.8.8.tgz#6d002340bfa0e2d09f7225bb72b003a5003ddb84"
integrity sha512-pJ9Hs14wxvBQZxmw40yv/Ia7bAQGPk7RDWf0hysLa7C3OGbwTbWQhOwW5+rDaJGI5fMTgTQThUoihbZzb0CkJQ==
babel-plugin-transform-es2015-arrow-functions@^6.22.0:
version "6.22.0"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz#452692cb711d5f79dc7f85e440ce41b9f244d221"
......@@ -3543,25 +3553,27 @@ entities@^1.1.1, entities@~1.1.1:
integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==
enzyme-adapter-preact-pure@^1.9.0:
version "1.9.0"
resolved "https://registry.yarnpkg.com/enzyme-adapter-preact-pure/-/enzyme-adapter-preact-pure-1.9.0.tgz#f0363af1eb3f986f3c3d7459e46fce966c538a07"
integrity sha512-FXX++t9UDNxW35XzS6yEpj3duaigCfshI6QHbF7mDfgVAx9+bSSMoP9zghfZL57ZA1S2+cak5uwvfNmK3Hcyqg==
version "1.10.2"
resolved "https://registry.yarnpkg.com/enzyme-adapter-preact-pure/-/enzyme-adapter-preact-pure-1.10.2.tgz#100b414ba114591cbcc63c105e8d638c7cebca49"
integrity sha512-mzASaVRWhizkZ2Rrujg+KzrZauf0+0J0N0QSUDS+sGvRin29l9keaAj5IVLGA8cejab6SFUK7K1p0GSccnyI+A==
dependencies:
array.prototype.flatmap "^1.2.1"
preact-render-to-string "^4.1.0"
enzyme@^3.8.0:
version "3.8.0"
resolved "https://registry.yarnpkg.com/enzyme/-/enzyme-3.8.0.tgz#646d2d5d0798cb98fdec39afcee8a53237b47ad5"
integrity sha512-bfsWo5nHyZm1O1vnIsbwdfhU989jk+squU9NKvB+Puwo5j6/Wg9pN5CO0YJelm98Dao3NPjkDZk+vvgwpMwYxw==
version "3.9.0"
resolved "https://registry.yarnpkg.com/enzyme/-/enzyme-3.9.0.tgz#2b491f06ca966eb56b6510068c7894a7e0be3909"
integrity sha512-JqxI2BRFHbmiP7/UFqvsjxTirWoM1HfeaJrmVSZ9a1EADKkZgdPcAuISPMpoUiHlac9J4dYt81MC5BBIrbJGMg==
dependencies:
array.prototype.flat "^1.2.1"
cheerio "^1.0.0-rc.2"
function.prototype.name "^1.1.0"
has "^1.0.3"
html-element-map "^1.0.0"
is-boolean-object "^1.0.0"
is-callable "^1.1.4"
is-number-object "^1.0.3"
is-regex "^1.0.4"
is-string "^1.0.4"
is-subset "^0.1.1"
lodash.escape "^4.0.1"
......@@ -3712,6 +3724,11 @@ eslint-plugin-mocha@^5.2.1:
dependencies:
ramda "^0.26.1"
eslint-plugin-react-hooks@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-1.6.0.tgz#348efcda8fb426399ac7b8609607c7b4025a6f5f"
integrity sha512-lHBVRIaz5ibnIgNG07JNiAuBUeKhEf8l4etNx5vfAEwqQ5tcuK3jV9yjmopPgQDagQb7HwIuQVsE3IVcGrRnag==
eslint-plugin-react@^7.12.4:
version "7.12.4"
resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.12.4.tgz#b1ecf26479d61aee650da612e425c53a99f48c8c"
......@@ -3744,9 +3761,9 @@ eslint-visitor-keys@^1.0.0:
integrity sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==
eslint@^5.12.1:
version "5.15.3"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.15.3.tgz#c79c3909dc8a7fa3714fb340c11e30fd2526b8b5"
integrity sha512-vMGi0PjCHSokZxE0NLp2VneGw5sio7SSiDNgIUn2tC0XkWJRNOIoHIg3CliLVfXnJsiHxGAYrkw0PieAu8+KYQ==
version "5.16.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.16.0.tgz#a1e3ac1aae4a3fbd8296fcf8f7ab7314cbb6abea"
integrity sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg==
dependencies:
"@babel/code-frame" "^7.0.0"
ajv "^6.9.1"
......@@ -3768,7 +3785,7 @@ eslint@^5.12.1:
import-fresh "^3.0.0"
imurmurhash "^0.1.4"
inquirer "^6.2.2"
js-yaml "^3.12.0"
js-yaml "^3.13.0"
json-stable-stringify-without-jsonify "^1.0.1"
levn "^0.3.0"
lodash "^4.17.11"
......@@ -4913,14 +4930,7 @@ has-values@^1.0.0:
is-number "^3.0.0"
kind-of "^4.0.0"
has@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/has/-/has-1.0.1.tgz#8461733f538b0837c9361e39a9ab9e9704dc2f28"
integrity sha1-hGFzP1OLCDfJNh45qauelwTcLyg=
dependencies:
function-bind "^1.0.2"
has@^1.0.1, has@^1.0.3:
has@^1.0.0, has@^1.0.1, has@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
......@@ -5002,6 +5012,13 @@ hosted-git-info@^2.1.4:
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.6.0.tgz#23235b29ab230c576aab0d4f13fc046b0b038222"
integrity sha512-lIbgIIQA3lz5XaB6vxakj6sDHADJiZadYEJB+FgA+C4nubM1NwcuvUr9EJPmnH1skZqpqUzWborWo8EIUi0Sdw==
html-element-map@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/html-element-map/-/html-element-map-1.0.1.tgz#3c4fcb4874ebddfe4283b51c8994e7713782b592"
integrity sha512-BZSfdEm6n706/lBfXKWa4frZRZcT5k1cOusw95ijZsHlI+GdgY0v95h6IzO3iIDf2ROwq570YTwqNPqHcNMozw==
dependencies:
array-filter "^1.0.0"
html-minifier@3.5.2:
version "3.5.2"
resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-3.5.2.tgz#d73bc3ff448942408818ce609bf3fb0ea7ef4eb7"
......@@ -5766,10 +5783,10 @@ js-yaml@3.12.0:
argparse "^1.0.7"
esprima "^4.0.0"
js-yaml@^3.12.0, js-yaml@^3.9.0:
version "3.12.1"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.1.tgz#295c8632a18a23e054cf5c9d3cecafe678167600"
integrity sha512-um46hB9wNOKlwkHgiuyEVAybXBjwFUV0Z/RaHJblRd9DXltue9FTYvzCr9ErQrK9Adz5MU4gHWVaNUfdmrC8qA==
js-yaml@^3.12.0, js-yaml@^3.13.0, js-yaml@^3.9.0:
version "3.13.0"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.0.tgz#38ee7178ac0eea2c97ff6d96fff4b18c7d8cf98e"
integrity sha512-pZZoSxcCYco+DIKBTimr67J6Hy+EYGZDY/HCWC+iAEA9h1ByhMXAIVUXMcMFpOCxQ/xjXmPI2MkDL5HRm5eFrQ==
dependencies:
argparse "^1.0.7"
esprima "^4.0.0"
......@@ -8749,13 +8766,13 @@ simple-is@~0.2.0:
integrity sha1-Krt1qt453rXMgVzhDmGRFkhQuvA=
sinon@^7.2.3:
version "7.2.7"
resolved "https://registry.yarnpkg.com/sinon/-/sinon-7.2.7.tgz#ee90f83ce87d9a6bac42cf32a3103d8c8b1bfb68"
integrity sha512-rlrre9F80pIQr3M36gOdoCEWzFAMDgHYD8+tocqOw+Zw9OZ8F84a80Ds69eZfcjnzDqqG88ulFld0oin/6rG/g==
version "7.3.1"
resolved "https://registry.yarnpkg.com/sinon/-/sinon-7.3.1.tgz#e8276522104e6c08d1cb52a907270b0e316655c4"
integrity sha512-eQKMaeWovtOtYe2xThEvaHmmxf870Di+bim10c3ZPrL5bZhLGtu8cz+rOBTFz0CwBV4Q/7dYwZiqZbGVLZ+vjQ==
dependencies:
"@sinonjs/commons" "^1.3.1"
"@sinonjs/commons" "^1.4.0"
"@sinonjs/formatio" "^3.2.1"
"@sinonjs/samsam" "^3.2.0"
"@sinonjs/samsam" "^3.3.1"
diff "^3.5.0"
lolex "^3.1.0"
nise "^1.4.10"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment