+ Quote includes a 0.875% MetaMask fee
+
+
diff --git a/ui/app/pages/swaps/fee-card/fee-card.test.js b/ui/app/pages/swaps/fee-card/fee-card.test.js
index 391cf5b7d..15c8b0c33 100644
--- a/ui/app/pages/swaps/fee-card/fee-card.test.js
+++ b/ui/app/pages/swaps/fee-card/fee-card.test.js
@@ -1,31 +1,61 @@
import React from 'react';
-import { render } from '@testing-library/react';
+import { renderWithProvider } from '../../../../../test/jest';
import { MAINNET_CHAIN_ID } from '../../../../../shared/constants/network';
-import FeeCard from './fee-card';
+import FeeCard from '.';
-describe('FeeCard', () => {
- const createProps = (customProps = {}) => {
- return {
- primaryFee: '1 ETH',
- secondaryFee: '2500 USD',
- hideTokenApprovalRow: false,
- onFeeCardMaxRowClick: jest.fn(),
- tokenApprovalTextComponent: <>>,
- tokenApprovalSourceTokenSymbol: 'ABC',
- onTokenApprovalClick: jest.fn(),
- metaMaskFee: '0.875',
- isBestQuote: true,
- numberOfQuotes: 6,
- onQuotesClick: jest.fn(),
- tokenConversionRate: 0.015,
- chainId: MAINNET_CHAIN_ID,
- ...customProps,
- };
+const createProps = (customProps = {}) => {
+ return {
+ primaryFee: {
+ fee: '0.0441 ETH',
+ maxFee: '0.04851 ETH',
+ },
+ secondaryFee: {
+ fee: '$101.98',
+ maxFee: '$112.17',
+ },
+ hideTokenApprovalRow: false,
+ onFeeCardMaxRowClick: jest.fn(),
+ tokenApprovalTextComponent: (
+
+ ABC
+
+ ),
+ tokenApprovalSourceTokenSymbol: 'ABC',
+ onTokenApprovalClick: jest.fn(),
+ metaMaskFee: '0.875',
+ isBestQuote: true,
+ numberOfQuotes: 6,
+ onQuotesClick: jest.fn(),
+ tokenConversionRate: 0.015,
+ chainId: MAINNET_CHAIN_ID,
+ ...customProps,
};
+};
+describe('FeeCard', () => {
it('renders the component with initial props', () => {
- const { container } = render(
);
- expect(container).toMatchSnapshot();
+ const props = createProps();
+ const { getByText } = renderWithProvider(
);
+ expect(getByText('Using the best quote')).toBeInTheDocument();
+ expect(getByText('6 quotes')).toBeInTheDocument();
+ expect(getByText('Max network fee')).toBeInTheDocument();
+ expect(getByText('Estimated network fee')).toBeInTheDocument();
+ expect(getByText(props.primaryFee.fee)).toBeInTheDocument();
+ expect(getByText(props.primaryFee.maxFee)).toBeInTheDocument();
+ expect(getByText(props.secondaryFee.fee)).toBeInTheDocument();
+ expect(getByText(props.secondaryFee.maxFee)).toBeInTheDocument();
+ expect(
+ getByText('Quote includes a 0.875% MetaMask fee'),
+ ).toBeInTheDocument();
+ expect(
+ document.querySelector('.fee-card__savings-and-quotes-header'),
+ ).toMatchSnapshot();
+ expect(
+ document.querySelector('.fee-card__top-bordered-row'),
+ ).toMatchSnapshot();
});
});
diff --git a/ui/app/pages/swaps/intro-popup/__snapshots__/intro-popup.test.js.snap b/ui/app/pages/swaps/intro-popup/__snapshots__/intro-popup.test.js.snap
new file mode 100644
index 000000000..d9d0324df
--- /dev/null
+++ b/ui/app/pages/swaps/intro-popup/__snapshots__/intro-popup.test.js.snap
@@ -0,0 +1,9 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`IntroPopup renders the component with initial props 1`] = `
+
+
+
+`;
diff --git a/ui/app/pages/swaps/intro-popup/intro-popup.test.js b/ui/app/pages/swaps/intro-popup/intro-popup.test.js
new file mode 100644
index 000000000..2ceec7e0e
--- /dev/null
+++ b/ui/app/pages/swaps/intro-popup/intro-popup.test.js
@@ -0,0 +1,24 @@
+import React from 'react';
+import configureMockStore from 'redux-mock-store';
+
+import {
+ renderWithProvider,
+ createSwapsMockStore,
+} from '../../../../../test/jest';
+import IntroPopup from '.';
+
+const createProps = (customProps = {}) => {
+ return {
+ onClose: jest.fn(),
+ ...customProps,
+ };
+};
+
+describe('IntroPopup', () => {
+ it('renders the component with initial props', () => {
+ const store = configureMockStore()(createSwapsMockStore());
+ const props = createProps();
+ const { container } = renderWithProvider(
, store);
+ expect(container).toMatchSnapshot();
+ });
+});
diff --git a/ui/app/pages/swaps/main-quote-summary/__snapshots__/main-quote-summary.test.js.snap b/ui/app/pages/swaps/main-quote-summary/__snapshots__/main-quote-summary.test.js.snap
new file mode 100644
index 000000000..36a520f6f
--- /dev/null
+++ b/ui/app/pages/swaps/main-quote-summary/__snapshots__/main-quote-summary.test.js.snap
@@ -0,0 +1,109 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`MainQuoteSummary renders the component with initial props 1`] = `
+
+
+ 2
+
+
+ E
+
+
+ ETH
+
+
+`;
+
+exports[`MainQuoteSummary renders the component with initial props 2`] = `
+
+
+ B
+
+
+ BAT
+
+
+`;
+
+exports[`MainQuoteSummary renders the component with initial props 3`] = `
+
+`;
+
+exports[`MainQuoteSummary renders the component with initial props 4`] = `
+
+
+
+ 1
+
+
+ ETH
+
+
+ =
+
+
+ 0.1
+
+
+ BAT
+
+
+
+
+`;
diff --git a/ui/app/pages/swaps/main-quote-summary/main-quote-summary.test.js b/ui/app/pages/swaps/main-quote-summary/main-quote-summary.test.js
new file mode 100644
index 000000000..cc1775363
--- /dev/null
+++ b/ui/app/pages/swaps/main-quote-summary/main-quote-summary.test.js
@@ -0,0 +1,39 @@
+import React from 'react';
+
+import { renderWithProvider } from '../../../../../test/jest';
+import MainQuoteSummary from '.';
+
+const createProps = (customProps = {}) => {
+ return {
+ sourceValue: '2000000000000000000',
+ sourceDecimals: 18,
+ sourceSymbol: 'ETH',
+ destinationValue: '200000000000000000',
+ destinationDecimals: 18,
+ destinationSymbol: 'BAT',
+ ...customProps,
+ };
+};
+
+describe('MainQuoteSummary', () => {
+ it('renders the component with initial props', () => {
+ const props = createProps();
+ const { getAllByText } = renderWithProvider(
+
,
+ );
+ expect(getAllByText(props.sourceSymbol)).toHaveLength(2);
+ expect(getAllByText(props.destinationSymbol)).toHaveLength(2);
+ expect(
+ document.querySelector('.main-quote-summary__source-row'),
+ ).toMatchSnapshot();
+ expect(
+ document.querySelector('.main-quote-summary__destination-row'),
+ ).toMatchSnapshot();
+ expect(
+ document.querySelector('.main-quote-summary__quote-large'),
+ ).toMatchSnapshot();
+ expect(
+ document.querySelector('.main-quote-summary__exchange-rate-container'),
+ ).toMatchSnapshot();
+ });
+});
diff --git a/ui/app/pages/swaps/searchable-item-list/__snapshots__/searchable-item-list.test.js.snap b/ui/app/pages/swaps/searchable-item-list/__snapshots__/searchable-item-list.test.js.snap
new file mode 100644
index 000000000..29affeb5d
--- /dev/null
+++ b/ui/app/pages/swaps/searchable-item-list/__snapshots__/searchable-item-list.test.js.snap
@@ -0,0 +1,77 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`SearchableItemList renders the component with initial props 1`] = `
+
+`;
+
+exports[`SearchableItemList renders the component with initial props 2`] = `
+
+
+
+
+
+ primaryLabel
+
+
+ secondaryLabel
+
+
+
+
+ rightPrimaryLabel
+
+
+ rightSecondaryLabel
+
+
+
+
+`;
diff --git a/ui/app/pages/swaps/searchable-item-list/searchable-item-list.test.js b/ui/app/pages/swaps/searchable-item-list/searchable-item-list.test.js
new file mode 100644
index 000000000..ef9a3a016
--- /dev/null
+++ b/ui/app/pages/swaps/searchable-item-list/searchable-item-list.test.js
@@ -0,0 +1,60 @@
+import React from 'react';
+
+import { renderWithProvider } from '../../../../../test/jest';
+import SearchableItemList from '.';
+
+const createProps = (customProps = {}) => {
+ return {
+ defaultToAll: true,
+ listTitle: 'listTitle',
+ itemsToSearch: [
+ {
+ iconUrl: 'iconUrl',
+ selected: true,
+ primaryLabel: 'primaryLabel',
+ secondaryLabel: 'secondaryLabel',
+ rightPrimaryLabel: 'rightPrimaryLabel',
+ rightSecondaryLabel: 'rightSecondaryLabel',
+ },
+ ],
+ fuseSearchKeys: [
+ {
+ name: 'name',
+ weight: 0.499,
+ },
+ {
+ name: 'symbol',
+ weight: 0.499,
+ },
+ {
+ name: 'address',
+ weight: 0.002,
+ },
+ ],
+ ...customProps,
+ };
+};
+
+describe('SearchableItemList', () => {
+ it('renders the component with initial props', () => {
+ const props = createProps();
+ const { getByText } = renderWithProvider(
);
+ expect(getByText(props.listTitle)).toBeInTheDocument();
+ expect(getByText(props.itemsToSearch[0].primaryLabel)).toBeInTheDocument();
+ expect(
+ getByText(props.itemsToSearch[0].secondaryLabel),
+ ).toBeInTheDocument();
+ expect(
+ getByText(props.itemsToSearch[0].rightPrimaryLabel),
+ ).toBeInTheDocument();
+ expect(
+ getByText(props.itemsToSearch[0].rightSecondaryLabel),
+ ).toBeInTheDocument();
+ expect(
+ document.querySelector('.searchable-item-list__search'),
+ ).toMatchSnapshot();
+ expect(
+ document.querySelector('.searchable-item-list__item'),
+ ).toMatchSnapshot();
+ });
+});
diff --git a/ui/app/pages/swaps/select-quote-popover/__snapshots__/select-quote-popover.test.js.snap b/ui/app/pages/swaps/select-quote-popover/__snapshots__/select-quote-popover.test.js.snap
new file mode 100644
index 000000000..d6b1172e5
--- /dev/null
+++ b/ui/app/pages/swaps/select-quote-popover/__snapshots__/select-quote-popover.test.js.snap
@@ -0,0 +1,9 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`SelectQuotePopover renders the component with initial props 1`] = `
+
+`;
diff --git a/ui/app/pages/swaps/select-quote-popover/select-quote-popover.test.js b/ui/app/pages/swaps/select-quote-popover/select-quote-popover.test.js
new file mode 100644
index 000000000..b23a35f68
--- /dev/null
+++ b/ui/app/pages/swaps/select-quote-popover/select-quote-popover.test.js
@@ -0,0 +1,24 @@
+import React from 'react';
+
+import { renderWithProvider } from '../../../../../test/jest';
+import SelectQuotePopover from '.';
+
+const createProps = (customProps = {}) => {
+ return {
+ onClose: jest.fn(),
+ onSubmit: jest.fn(),
+ swapToSymbol: 'ETH',
+ initialAggId: 'initialAggId',
+ onQuoteDetailsIsOpened: jest.fn(),
+ ...customProps,
+ };
+};
+
+describe('SelectQuotePopover', () => {
+ it('renders the component with initial props', () => {
+ const { container } = renderWithProvider(
+
,
+ );
+ expect(container).toMatchSnapshot();
+ });
+});
diff --git a/ui/app/pages/swaps/slippage-buttons/__snapshots__/slippage-buttons.test.js.snap b/ui/app/pages/swaps/slippage-buttons/__snapshots__/slippage-buttons.test.js.snap
new file mode 100644
index 000000000..7dd4d9bab
--- /dev/null
+++ b/ui/app/pages/swaps/slippage-buttons/__snapshots__/slippage-buttons.test.js.snap
@@ -0,0 +1,42 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`SlippageButtons renders the component with initial props 1`] = `
+
+`;
+
+exports[`SlippageButtons renders the component with initial props 2`] = `
+
+
+
+
+
+`;
diff --git a/ui/app/pages/swaps/slippage-buttons/slippage-buttons.test.js b/ui/app/pages/swaps/slippage-buttons/slippage-buttons.test.js
new file mode 100644
index 000000000..0108f07d2
--- /dev/null
+++ b/ui/app/pages/swaps/slippage-buttons/slippage-buttons.test.js
@@ -0,0 +1,31 @@
+import React from 'react';
+
+import { renderWithProvider } from '../../../../../test/jest';
+import SlippageButtons from '.';
+
+const createProps = (customProps = {}) => {
+ return {
+ onSelect: jest.fn(),
+ maxAllowedSlippage: 15,
+ currentSlippage: 3,
+ ...customProps,
+ };
+};
+
+describe('SlippageButtons', () => {
+ it('renders the component with initial props', () => {
+ const { getByText } = renderWithProvider(
+
,
+ );
+ expect(getByText('2%')).toBeInTheDocument();
+ expect(getByText('3%')).toBeInTheDocument();
+ expect(getByText('custom')).toBeInTheDocument();
+ expect(getByText('Advanced Options')).toBeInTheDocument();
+ expect(
+ document.querySelector('.slippage-buttons__header'),
+ ).toMatchSnapshot();
+ expect(
+ document.querySelector('.slippage-buttons__button-group'),
+ ).toMatchSnapshot();
+ });
+});
diff --git a/ui/app/pages/swaps/swaps-footer/__snapshots__/swaps-footer.test.js.snap b/ui/app/pages/swaps/swaps-footer/__snapshots__/swaps-footer.test.js.snap
new file mode 100644
index 000000000..3edd2af66
--- /dev/null
+++ b/ui/app/pages/swaps/swaps-footer/__snapshots__/swaps-footer.test.js.snap
@@ -0,0 +1,41 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`SwapsFooter renders the component with initial props 1`] = `
+
+
+
+`;
diff --git a/ui/app/pages/swaps/swaps-footer/swaps-footer.test.js b/ui/app/pages/swaps/swaps-footer/swaps-footer.test.js
new file mode 100644
index 000000000..4cff41083
--- /dev/null
+++ b/ui/app/pages/swaps/swaps-footer/swaps-footer.test.js
@@ -0,0 +1,28 @@
+import React from 'react';
+
+import { renderWithProvider } from '../../../../../test/jest';
+import SwapsFooter from '.';
+
+const createProps = (customProps = {}) => {
+ return {
+ onCancel: jest.fn(),
+ onSubmit: jest.fn(),
+ submitText: 'submitText',
+ disabled: false,
+ showTermsOfService: true,
+ ...customProps,
+ };
+};
+
+describe('SwapsFooter', () => {
+ it('renders the component with initial props', () => {
+ const props = createProps();
+ const { container, getByText } = renderWithProvider(
+
,
+ );
+ expect(getByText(props.submitText)).toBeInTheDocument();
+ expect(getByText('Back')).toBeInTheDocument();
+ expect(getByText('Terms of Service')).toBeInTheDocument();
+ expect(container).toMatchSnapshot();
+ });
+});
diff --git a/yarn.lock b/yarn.lock
index 4701fd8ff..6aac67d17 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3522,6 +3522,20 @@
dom-accessibility-api "^0.5.0"
pretty-format "^25.5.0"
+"@testing-library/jest-dom@^5.11.10":
+ version "5.11.10"
+ resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.11.10.tgz#1cd90715023e1627f5ed26ab3b38e6f22d77046c"
+ integrity sha512-FuKiq5xuk44Fqm0000Z9w0hjOdwZRNzgx7xGGxQYepWFZy+OYUMOT/wPI4nLYXCaVltNVpU1W/qmD88wLWDsqQ==
+ dependencies:
+ "@babel/runtime" "^7.9.2"
+ "@types/testing-library__jest-dom" "^5.9.1"
+ aria-query "^4.2.2"
+ chalk "^3.0.0"
+ css "^3.0.0"
+ css.escape "^1.5.1"
+ lodash "^4.17.15"
+ redent "^3.0.0"
+
"@testing-library/react-hooks@^3.2.1":
version "3.2.1"
resolved "https://registry.yarnpkg.com/@testing-library/react-hooks/-/react-hooks-3.2.1.tgz#19b6caa048ef15faa69d439c469033873ea01294"
@@ -3720,6 +3734,14 @@
dependencies:
"@types/istanbul-lib-report" "*"
+"@types/jest@*":
+ version "26.0.22"
+ resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.22.tgz#8308a1debdf1b807aa47be2838acdcd91e88fbe6"
+ integrity sha512-eeWwWjlqxvBxc4oQdkueW5OF/gtfSceKk4OnOAGlUSwS/liBRtZppbJuz1YkgbrbfGOoeBHun9fOvXnjNwrSOw==
+ dependencies:
+ jest-diff "^26.0.0"
+ pretty-format "^26.0.0"
+
"@types/json-schema@^7.0.3":
version "7.0.7"
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad"
@@ -3906,6 +3928,13 @@
resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.6.tgz#a9ca4b70a18b270ccb2bc0aaafefd1d486b7ea74"
integrity sha512-W+bw9ds02rAQaMvaLYxAbJ6cvguW/iJXNT6lTssS1ps6QdrMKttqEAMEG/b5CR8TZl3/L7/lH0ZV5nNR1LXikA==
+"@types/testing-library__jest-dom@^5.9.1":
+ version "5.9.5"
+ resolved "https://registry.yarnpkg.com/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.9.5.tgz#5bf25c91ad2d7b38f264b12275e5c92a66d849b0"
+ integrity sha512-ggn3ws+yRbOHog9GxnXiEZ/35Mow6YtPZpd7Z5mKDeZS/o7zx3yAle0ov/wjhVB5QT4N2Dt+GNoGCdqkBGCajQ==
+ dependencies:
+ "@types/jest" "*"
+
"@types/testing-library__react-hooks@^3.0.0":
version "3.2.0"
resolved "https://registry.yarnpkg.com/@types/testing-library__react-hooks/-/testing-library__react-hooks-3.2.0.tgz#52f3a109bef06080e3b1e3ae7ea1c014ce859897"
@@ -5299,6 +5328,11 @@ atob@^2.0.0:
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.1.tgz#ae2d5a729477f289d60dd7f96a6314a22dd6c22a"
integrity sha1-ri1acpR38onWDdf5amMUoi3Wwio=
+atob@^2.1.2:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
+ integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
+
autoprefixer@^8.0.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-8.1.0.tgz#374cf35be1c0e8fce97408d876f95f66f5cb4641"
@@ -8492,6 +8526,11 @@ css-what@2.1:
resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2"
integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==
+css.escape@^1.5.1:
+ version "1.5.1"
+ resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb"
+ integrity sha1-QuJ9T6BK4y+TGktNQZH6nN3ul8s=
+
css@2.X, css@^2.0.0, css@^2.2.1:
version "2.2.3"
resolved "https://registry.yarnpkg.com/css/-/css-2.2.3.tgz#f861f4ba61e79bedc962aa548e5780fd95cbc6be"
@@ -8502,6 +8541,15 @@ css@2.X, css@^2.0.0, css@^2.2.1:
source-map-resolve "^0.5.1"
urix "^0.1.0"
+css@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/css/-/css-3.0.0.tgz#4447a4d58fdd03367c516ca9f64ae365cee4aa5d"
+ integrity sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==
+ dependencies:
+ inherits "^2.0.4"
+ source-map "^0.6.1"
+ source-map-resolve "^0.6.0"
+
cssesc@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
@@ -15490,7 +15538,7 @@ jest-config@^26.6.3:
micromatch "^4.0.2"
pretty-format "^26.6.2"
-jest-diff@^26.6.2:
+jest-diff@^26.0.0, jest-diff@^26.6.2:
version "26.6.2"
resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.6.2.tgz#1aa7468b52c3a68d7d5c5fdcdfcd5e49bd164394"
integrity sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==
@@ -20905,7 +20953,7 @@ pretty-format@^25.5.0:
ansi-styles "^4.0.0"
react-is "^16.12.0"
-pretty-format@^26.6.2:
+pretty-format@^26.0.0, pretty-format@^26.6.2:
version "26.6.2"
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93"
integrity sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==
@@ -23984,6 +24032,14 @@ source-map-resolve@^0.5.0, source-map-resolve@^0.5.1:
source-map-url "^0.4.0"
urix "^0.1.0"
+source-map-resolve@^0.6.0:
+ version "0.6.0"
+ resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.6.0.tgz#3d9df87e236b53f16d01e58150fc7711138e5ed2"
+ integrity sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==
+ dependencies:
+ atob "^2.1.2"
+ decode-uri-component "^0.2.0"
+
source-map-support@0.5.12:
version "0.5.12"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.12.tgz#b4f3b10d51857a5af0138d3ce8003b201613d599"