1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-11-23 02:10:12 +01:00
metamask-extension/ui/helpers/utils/util.test.js
vthomas13 532a10f9f5
Adding browser outdated notification (#17027)
* Adding browser outdated notification

* updating dependency

* adding unit tests for util function

* adding unit tests for selectors, lintfix

* Added Tests, refactored notification delay logic

* lint:fix

* adding test coverage for method parameter

* Update app/scripts/controllers/app-state.js

adding documentation details

Co-authored-by: Mark Stacey <markjstacey@gmail.com>

* moving declaration into test

* Update app/scripts/controllers/app-state.test.js

spacing in test file

Co-authored-by: Mark Stacey <markjstacey@gmail.com>

* Update jest.config.js

removing duplicate entries

Co-authored-by: Mark Stacey <markjstacey@gmail.com>

* using async submitRequestToBackground method

* removing unused import

* removing unnecessary link syntax in notification

* adding opera and edge and associated tests

* handling the undefined case in bowser.satisfies

* setOutdatedBrowserWarningLastShown try/catch

* lint:fix

* Removing try/catch and letting errors bubble up

Removing deprecated displayWarning method

Co-authored-by: Mark Stacey <markjstacey@gmail.com>

* taking out forceMetamaskUpdateState call

* excludint app-state test from mocha test suite

* Added note: Jest files should match Mocha excluded

* syntax error, lint:fix

---------

Co-authored-by: Mark Stacey <markjstacey@gmail.com>
2023-02-02 13:56:41 -05:00

569 lines
21 KiB
JavaScript

import Bowser from 'bowser';
import { BN } from 'ethereumjs-util';
import { addHexPrefixToObjectValues } from '../../../shared/lib/swaps-utils';
import { toPrecisionWithoutTrailingZeros } from '../../../shared/lib/transactions-controller-utils';
import * as util from './util';
describe('util', () => {
let ethInWei = '1';
for (let i = 0; i < 18; i++) {
ethInWei += '0';
}
describe('#parseBalance', () => {
it('should render 0.01 eth correctly', () => {
const input = '0x2386F26FC10000';
const output = util.parseBalance(input);
expect(output).toStrictEqual(['0', '01']);
});
it('should render 12.023 eth correctly', () => {
const input = 'A6DA46CCA6858000';
const output = util.parseBalance(input);
expect(output).toStrictEqual(['12', '023']);
});
it('should render 0.0000000342422 eth correctly', () => {
const input = '0x7F8FE81C0';
const output = util.parseBalance(input);
expect(output).toStrictEqual(['0', '0000000342422']);
});
it('should render 0 eth correctly', () => {
const input = '0x0';
const output = util.parseBalance(input);
expect(output).toStrictEqual(['0', '0']);
});
});
describe('#addressSummary', () => {
it('should add case-sensitive checksum', () => {
const address = '0xfdea65c8e26263f6d9a1b5de9555d2931a33b825';
const result = util.addressSummary(address);
expect(result).toStrictEqual('0xFDEa65C8...b825');
});
it('should accept arguments for firstseg, lastseg, and keepPrefix', () => {
const address = '0xfdea65c8e26263f6d9a1b5de9555d2931a33b825';
const result = util.addressSummary(address, 4, 4, false);
expect(result).toStrictEqual('FDEa...b825');
});
});
describe('isValidDomainName', () => {
it('should return true when given a valid domain name', () => {
expect(util.isValidDomainName('foo.bar')).toStrictEqual(true);
});
it('should return true when given a valid subdomain', () => {
expect(util.isValidDomainName('foo.foo.bar')).toStrictEqual(true);
});
it('should return true when given a single-character domain', () => {
expect(util.isValidDomainName('f.bar')).toStrictEqual(true);
});
it('should return true when given a unicode TLD', () => {
expect(util.isValidDomainName('台灣.中国')).toStrictEqual(true);
});
it('should return false when given a domain with unacceptable ASCII characters', () => {
expect(util.isValidDomainName('$.bar')).toStrictEqual(false);
});
it('should return false when given a TLD that starts with a dash', () => {
expect(util.isValidDomainName('foo.-bar')).toStrictEqual(false);
});
it('should return false when given a TLD that ends with a dash', () => {
expect(util.isValidDomainName('foo.bar-')).toStrictEqual(false);
});
it('should return false when given a domain name with a chunk that starts with a dash', () => {
expect(util.isValidDomainName('-foo.bar')).toStrictEqual(false);
});
it('should return false when given a domain name with a chunk that ends with a dash', () => {
expect(util.isValidDomainName('foo-.bar')).toStrictEqual(false);
});
it('should return false when given a bare TLD', () => {
expect(util.isValidDomainName('bar')).toStrictEqual(false);
});
it('should return false when given a domain that starts with a period', () => {
expect(util.isValidDomainName('.bar')).toStrictEqual(false);
});
it('should return false when given a subdomain that starts with a period', () => {
expect(util.isValidDomainName('.foo.bar')).toStrictEqual(false);
});
it('should return false when given a domain that ends with a period', () => {
expect(util.isValidDomainName('bar.')).toStrictEqual(false);
});
it('should return false when given a 1-character TLD', () => {
expect(util.isValidDomainName('foo.b')).toStrictEqual(false);
});
});
describe('isOriginContractAddress', () => {
it('should return true when the send address is the same as the selected tokens contract address', () => {
expect(
util.isOriginContractAddress(
'0x8d6b81208414189a58339873ab429b6c47ab92d3',
'0x8d6b81208414189a58339873ab429b6c47ab92d3',
),
).toStrictEqual(true);
});
it('should return true when the send address is the same as the selected tokens contract address, capitalized input', () => {
expect(
util.isOriginContractAddress(
'0x8d6b81208414189a58339873ab429b6c47ab92d3',
'0X8D6B81208414189A58339873AB429B6C47AB92D3',
),
).toStrictEqual(true);
});
it('should return false when the recipient address differs', () => {
expect(
util.isOriginContractAddress(
'0x8d6b81208414189a58339873ab429b6c47ab92d3',
'0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B',
),
).toStrictEqual(false);
});
});
describe('#numericBalance', () => {
it('should return a BN 0 if given nothing', () => {
const result = util.numericBalance();
expect(result.toString(10)).toStrictEqual('0');
});
it('should work with hex prefix', () => {
const result = util.numericBalance('0x012');
expect(result.toString(10)).toStrictEqual('18');
});
it('should work with no hex prefix', () => {
const result = util.numericBalance('012');
expect(result.toString(10)).toStrictEqual('18');
});
});
describe('#formatBalance', () => {
it('should return None when given nothing', () => {
const result = util.formatBalance();
expect(result).toStrictEqual('None', 'should return "None"');
});
it('should return 1.0000 ETH', () => {
const input = new BN(ethInWei, 10).toJSON();
const result = util.formatBalance(input, 4);
expect(result).toStrictEqual('1.0000 ETH');
});
it('should return 0.500 ETH', function () {
const input = new BN(ethInWei, 10).div(new BN('2', 10)).toJSON();
const result = util.formatBalance(input, 3);
expect(result).toStrictEqual('0.500 ETH');
});
it('should display specified decimal points', () => {
const input = '0x128dfa6a90b28000';
const result = util.formatBalance(input, 2);
expect(result).toStrictEqual('1.33 ETH');
});
it('should default to 3 decimal points', () => {
const input = '0x128dfa6a90b28000';
const result = util.formatBalance(input);
expect(result).toStrictEqual('1.337 ETH');
});
it('should show 2 significant digits for tiny balances', () => {
const input = '0x1230fa6a90b28';
const result = util.formatBalance(input);
expect(result).toStrictEqual('0.00032 ETH');
});
it('should not parse the balance and return value with 2 decimal points with ETH at the end', () => {
const value = '1.2456789';
const needsParse = false;
const result = util.formatBalance(value, 2, needsParse);
expect(result).toStrictEqual('1.24 ETH');
});
});
describe('#getIsBrowserDeprecated', () => {
it('should call Bowser.getParser when no parameter is passed', () => {
const spy = jest.spyOn(Bowser, 'getParser');
util.getIsBrowserDeprecated();
expect(spy).toHaveBeenCalled();
});
it('should return false when given a modern chrome browser', () => {
const browser = Bowser.getParser(
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.2623.112 Safari/537.36',
);
const result = util.getIsBrowserDeprecated(browser);
expect(result).toStrictEqual(false);
});
it('should return true when given an outdated chrome browser', () => {
const browser = Bowser.getParser(
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.2623.112 Safari/537.36',
);
const result = util.getIsBrowserDeprecated(browser);
expect(result).toStrictEqual(true);
});
it('should return false when given a modern firefox browser', () => {
const browser = Bowser.getParser(
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0',
);
const result = util.getIsBrowserDeprecated(browser);
expect(result).toStrictEqual(false);
});
it('should return true when given an outdated firefox browser', () => {
const browser = Bowser.getParser(
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0',
);
const result = util.getIsBrowserDeprecated(browser);
expect(result).toStrictEqual(true);
});
it('should return false when given a modern opera browser', () => {
const browser = Bowser.getParser(
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_16_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.3578.98 Safari/537.36 OPR/68.0.3135.47',
);
const result = util.getIsBrowserDeprecated(browser);
expect(result).toStrictEqual(false);
});
it('should return true when given an outdated opera browser', () => {
const browser = Bowser.getParser(
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_16_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36 OPR/58.0.3135.47',
);
const result = util.getIsBrowserDeprecated(browser);
expect(result).toStrictEqual(true);
});
it('should return false when given a modern edge browser', () => {
const browser = Bowser.getParser(
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.3578.98 Safari/537.36 Edg/81.0.416.68',
);
const result = util.getIsBrowserDeprecated(browser);
expect(result).toStrictEqual(false);
});
it('should return true when given an outdated edge browser', () => {
const browser = Bowser.getParser(
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36 Edge/71.0.416.68',
);
const result = util.getIsBrowserDeprecated(browser);
expect(result).toStrictEqual(true);
});
it('should return false when given an unknown browser', () => {
const browser = Bowser.getParser(
'Mozilla/5.0 (Nintendo Switch; WebApplet) AppleWebKit/609.4 (KHTML, like Gecko) NF/6.0.2.21.3 NintendoBrowser/5.1.0.22474',
);
const result = util.getIsBrowserDeprecated(browser);
expect(result).toStrictEqual(false);
});
});
describe('normalizing values', function () {
describe('#getRandomFileName', () => {
it('should only return a string containing alphanumeric characters', () => {
const result = util.getRandomFileName();
expect(result[0]).toStrictEqual(
expect.stringMatching(/^[a-zA-Z0-9]*$/gu),
);
});
// 50 samples
it('should return a string that is between 6 and 12 characters in length', () => {
for (let i = 0; i < 50; i++) {
const result = util.getRandomFileName();
expect(result.length >= 6 && result.length <= 12).toStrictEqual(true);
}
});
});
});
describe('checkExistingAddresses', () => {
const tokenList = [
{ address: 'A' },
{ address: 'n' },
{ address: 'Q' },
{ address: 'z' },
];
it('should return true when a lowercase address matches an uppercase address in the passed list', () => {
expect(util.checkExistingAddresses('q', tokenList)).toStrictEqual(true);
});
it('should return true when an uppercase address matches a lowercase address in the passed list', () => {
expect(util.checkExistingAddresses('N', tokenList)).toStrictEqual(true);
});
it('should return true when a lowercase address matches a lowercase address in the passed list', () => {
expect(util.checkExistingAddresses('z', tokenList)).toStrictEqual(true);
});
it('should return true when an uppercase address matches an uppercase address in the passed list', () => {
expect(util.checkExistingAddresses('Q', tokenList)).toStrictEqual(true);
});
it('should return false when the passed address is not in the passed list', () => {
expect(util.checkExistingAddresses('b', tokenList)).toStrictEqual(false);
});
});
describe('toPrecisionWithoutTrailingZeros', () => {
const testData = [
{ args: ['0', 9], result: '0' },
{ args: [0, 9], result: '0' },
{ args: ['0.0', 9], result: '0' },
{ args: ['0.000000000000', 9], result: '0' },
{ args: ['1', 9], result: '1' },
{ args: [1], result: '1' },
{ args: ['1.0', 9], result: '1' },
{ args: ['1.000000000', 9], result: '1' },
{ args: ['000000001', 9], result: '1' },
{ args: ['000000001.0', 9], result: '1' },
{ args: ['100000000', 9], result: '100000000' },
{ args: ['100000000.00001', 9], result: '100000000' },
{ args: ['100.00001', 9], result: '100.00001' },
{ args: ['100.00001000', 9], result: '100.00001' },
{ args: ['100.000010001', 9], result: '100.00001' },
{ args: ['10.010101', 9], result: '10.010101' },
{ args: ['0.1', 5], result: '0.1' },
{ args: ['0.10', 5], result: '0.1' },
{ args: ['0.1010', 5], result: '0.101' },
{ args: ['0.01001', 5], result: '0.01001' },
{ args: ['0.010010', 5], result: '0.01001' },
{ args: ['0.010011', 5], result: '0.010011' },
{ args: ['1.01005', 5], result: '1.0101' },
{ args: ['1.000049', 5], result: '1' },
{ args: ['1.00005', 5], result: '1.0001' },
{ args: ['0.0000123456789', 9], result: '0.0000123456789' },
{ args: ['1.0000123456789', 10], result: '1.000012346' },
{ args: ['10000.0000012345679', 10], result: '10000' },
{ args: ['1000000000000', 10], result: '1e+12' },
{ args: ['1000050000000', 10], result: '1.00005e+12' },
{ args: ['100000000000000000000', 10], result: '1e+20' },
{ args: ['100005000000000000000', 10], result: '1.00005e+20' },
{ args: ['100005000000000000000.0', 10], result: '1.00005e+20' },
];
testData.forEach(({ args, result }) => {
it(`should return ${result} when passed number ${args[0]} and precision ${args[1]}`, () => {
expect(toPrecisionWithoutTrailingZeros(...args)).toStrictEqual(result);
});
});
});
describe('addHexPrefixToObjectValues()', () => {
it('should return a new object with the same properties with a 0x prefix', () => {
expect(
addHexPrefixToObjectValues({
prop1: '0x123',
prop2: '456',
prop3: 'x',
}),
).toStrictEqual({
prop1: '0x123',
prop2: '0x456',
prop3: '0xx',
});
});
});
describe('toHumanReadableTime()', () => {
const t = (key, number) => {
switch (key) {
case 'gasTimingSecondsShort':
return `${number} sec`;
case 'gasTimingMinutesShort':
return `${number} min`;
case 'gasTimingHoursShort':
return `${number} hrs`;
default:
return '';
}
};
it('should return empty string if milliseconds passed is undefined', () => {
expect(util.toHumanReadableTime(t)).toStrictEqual('');
});
it('should return rounded value for time', () => {
expect(util.toHumanReadableTime(t, 6300)).toStrictEqual('7 sec');
});
it('should return value in seconds for milliseconds passed is < 9000', () => {
expect(util.toHumanReadableTime(t, 6000)).toStrictEqual('6 sec');
});
it('should return value in seconds for milliseconds passed is > 6000 and <= 9000', () => {
expect(util.toHumanReadableTime(t, 9000)).toStrictEqual('9 sec');
});
it('should return value in minutes for milliseconds passed is > 90000', () => {
expect(util.toHumanReadableTime(t, 90001)).toStrictEqual('2 min');
});
it('should return value in minutes for milliseconds passed is > 90000 and <= 5400000', () => {
expect(util.toHumanReadableTime(t, 5400000)).toStrictEqual('90 min');
});
it('should return value in hours for milliseconds passed is > 5400000', () => {
expect(util.toHumanReadableTime(t, 5400001)).toStrictEqual('2 hrs');
});
it('should return value in hours for milliseconds passed very high above 5400000', () => {
expect(util.toHumanReadableTime(t, 7200000)).toStrictEqual('2 hrs');
});
});
describe('sanitizeMessage', () => {
let message;
let primaryType;
let types;
beforeEach(() => {
message = {
contents: 'Hello, Bob!',
from: {
name: 'Cow',
wallets: [
'0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826',
'0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF',
],
},
to: [
{
name: 'Bob',
wallets: [
'0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB',
'0xB0BdaBea57B0BDABeA57b0bdABEA57b0BDabEa57',
'0xB0B0b0b0b0b0B000000000000000000000000000',
],
},
],
nestArray: [
[12, 34, 56],
[56, 78, 89],
],
};
primaryType = 'Mail';
types = {
EIP712Domain: [
{ name: 'name', type: 'string' },
{ name: 'version', type: 'string' },
{ name: 'chainId', type: 'uint256' },
{ name: 'verifyingContract', type: 'address' },
],
Mail: [
{ name: 'from', type: 'Person' },
{ name: 'to', type: 'Person[]' },
{ name: 'contents', type: 'string' },
{ name: 'nestArray', type: 'uint256[2][2]' },
{ name: 'nestedPeople', type: 'Person[][]' },
],
Person: [
{ name: 'name', type: 'string' },
{ name: 'wallets', type: 'address[]' },
],
};
});
it('should throw an error if types is undefined', () => {
expect(() =>
util.sanitizeMessage(message, primaryType, undefined),
).toThrow('Invalid types definition');
});
it('should throw an error if base type is not defined', () => {
expect(() => util.sanitizeMessage(message, undefined, types)).toThrow(
'Invalid primary type definition',
);
});
it('should return parsed message if types is defined', () => {
const result = util.sanitizeMessage(message, primaryType, types);
expect(result.contents).toStrictEqual('Hello, Bob!');
expect(result.from.name).toStrictEqual('Cow');
expect(result.from.wallets).toHaveLength(2);
expect(result.to).toHaveLength(1);
expect(result.to[0].name).toStrictEqual('Bob');
expect(result.to[0].wallets).toHaveLength(3);
});
it('should return parsed nested array if defined', () => {
const result = util.sanitizeMessage(
{
nestArray: [
[12, 34, 56],
[56, 78, 89],
],
},
primaryType,
types,
);
expect(result.nestArray).toHaveLength(2);
expect(result.nestArray[0]).toHaveLength(3);
expect(result.nestArray[0][0]).toStrictEqual(12);
expect(result.nestArray[0][2]).toStrictEqual(56);
});
it('should return parsed nested array with struct if defined', () => {
const msg = {
nestedPeople: [
[
{
name: 'Bob',
wallets: [
'0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB',
'0xB0BdaBea57B0BDABeA57b0bdABEA57b0BDabEa57',
'0xB0B0b0b0b0b0B000000000000000000000000000',
],
},
],
[
{
name: 'Ben',
wallets: [
'0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB',
'0xB0BdaBea57B0BDABeA57b0bdABEA57b0BDabEa57',
'0xB0B0b0b0b0b0B000000000000000000000000000',
],
},
{
name: 'Brandon',
wallets: [
'0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB',
'0xB0BdaBea57B0BDABeA57b0bdABEA57b0BDabEa57',
'0xB0B0b0b0b0b0B000000000000000000000000000',
],
},
],
],
};
const result = util.sanitizeMessage(msg, primaryType, types);
expect(result.nestedPeople).toHaveLength(2);
expect(result.nestedPeople[0]).toHaveLength(1);
expect(result.nestedPeople[0][0].name).toStrictEqual('Bob');
expect(result.nestedPeople[0][0].wallets).toHaveLength(3);
expect(result.nestedPeople[0][0].wallets[0]).toStrictEqual(
'0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB',
);
expect(result.nestedPeople[1][1].name).toStrictEqual('Brandon');
});
it('should return ignore message data with unknown types', () => {
message.do_not_display = 'one';
message.do_not_display_2 = {
do_not_display: 'two',
};
// result will NOT contain the do_not_displays because type definition
const result = util.sanitizeMessage(message, primaryType, types);
expect(result.contents).toStrictEqual('Hello, Bob!');
expect(result.from.name).toStrictEqual('Cow');
expect(result.from.wallets).toHaveLength(2);
expect(result.to).toHaveLength(1);
expect(result.to[0].name).toStrictEqual('Bob');
expect(result.to[0].wallets).toHaveLength(3);
expect(result.do_not_display).toBeUndefined();
expect(result.do_not_display_2).toBeUndefined();
});
});
});