Skip to content
This repository was archived by the owner on Mar 5, 2025. It is now read-only.

Commit 62c8c85

Browse files
add tests for Uint8Array in jsdom
1 parent 992d5bf commit 62c8c85

File tree

5 files changed

+885
-7
lines changed

5 files changed

+885
-7
lines changed
Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
/**
2+
* @jest-environment jsdom
3+
*/
4+
5+
/*
6+
This file is part of web3.js.
7+
8+
web3.js is free software: you can redistribute it and/or modify
9+
it under the terms of the GNU Lesser General Public License as published by
10+
the Free Software Foundation, either version 3 of the License, or
11+
(at your option) any later version.
12+
13+
web3.js is distributed in the hope that it will be useful,
14+
but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
GNU Lesser General Public License for more details.
17+
18+
You should have received a copy of the GNU Lesser General Public License
19+
along with web3.js. If not, see <http://www.gnu.org/licenses/>.
20+
*/
21+
22+
// this file contains the unit test for the event emitter in the DOM environment
23+
// it is executed in the jsdom environment (see "@jest-environment jsdom" in the top comment of this file)
24+
25+
// ignore the following rule to allow keeping `@jest-environment jsdom` on top:
26+
// eslint-disable-next-line header/header
27+
import { TextEncoder } from 'util';
28+
import crypto from 'crypto';
29+
// polyfill for jsdom
30+
global.TextEncoder = TextEncoder;
31+
// @ts-expect-error "Cannot assign to 'subtle' because it is a read-only property."
32+
global.crypto.subtle = crypto.webcrypto.subtle;
33+
34+
/* eslint-disable import/first */
35+
import { Address } from 'web3-types';
36+
import { Web3ValidatorError, isHexStrict } from 'web3-validator';
37+
import {
38+
create,
39+
decrypt,
40+
encrypt,
41+
hashMessage,
42+
privateKeyToAccount,
43+
privateKeyToAddress,
44+
recover,
45+
recoverTransaction,
46+
sign,
47+
signTransaction,
48+
privateKeyToPublicKey,
49+
} from '../../src/account';
50+
import {
51+
invalidDecryptData,
52+
invalidEncryptData,
53+
invalidKeyStore,
54+
invalidPrivateKeytoAccountData,
55+
invalidPrivateKeyToAddressData,
56+
signatureRecoverData,
57+
transactionsTestData,
58+
validDecryptData,
59+
validEncryptData,
60+
validHashMessageData,
61+
validPrivateKeytoAccountData,
62+
validPrivateKeyToAddressData,
63+
validPrivateKeyToPublicKeyData,
64+
validRecover,
65+
} from '../fixtures/account';
66+
import { TransactionFactory } from '../../src/tx/transactionFactory';
67+
import { TxData } from '../../src/tx/types';
68+
69+
describe('accounts', () => {
70+
describe('create', () => {
71+
describe('valid cases', () => {
72+
it('%s', () => {
73+
const account = create();
74+
expect(typeof account.privateKey).toBe('string');
75+
expect(typeof account.address).toBe('string');
76+
expect(isHexStrict(account.address)).toBe(true);
77+
expect(typeof account.encrypt).toBe('function');
78+
expect(typeof account.sign).toBe('function');
79+
expect(typeof account.signTransaction).toBe('function');
80+
});
81+
});
82+
});
83+
84+
describe('privateKeyToAddress', () => {
85+
describe('valid cases', () => {
86+
it.each(validPrivateKeyToAddressData)('%s', (input, output) => {
87+
expect(privateKeyToAddress(input)).toEqual(output);
88+
});
89+
});
90+
91+
describe('invalid cases', () => {
92+
it.each(invalidPrivateKeyToAddressData)('%s', (input, output) => {
93+
expect(() => privateKeyToAddress(input)).toThrow(output);
94+
});
95+
});
96+
});
97+
98+
describe('privateKeyToAccount', () => {
99+
describe('valid cases', () => {
100+
it.each(validPrivateKeytoAccountData)('%s', (input, output) => {
101+
expect(JSON.stringify(privateKeyToAccount(input.address, input.ignoreLength))).toEqual(
102+
JSON.stringify(output),
103+
);
104+
});
105+
});
106+
107+
describe('invalid cases', () => {
108+
it.each(invalidPrivateKeytoAccountData)('%s', (input, output) => {
109+
expect(() => privateKeyToAccount(input)).toThrow(output);
110+
});
111+
});
112+
});
113+
describe('privateKeyToPublicKey', () => {
114+
describe('valid cases', () => {
115+
it.each(validPrivateKeyToPublicKeyData)('%s', (privateKey, isCompressed, output) => {
116+
expect(privateKeyToPublicKey(privateKey, isCompressed)).toEqual(output);
117+
});
118+
});
119+
});
120+
121+
describe('Signing and Recovery of Transaction', () => {
122+
it.each(transactionsTestData)('sign transaction', async txData => {
123+
const account = create();
124+
125+
const signedResult = await signTransaction(
126+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
127+
TransactionFactory.fromTxData(txData as unknown as TxData),
128+
account.privateKey,
129+
);
130+
expect(signedResult).toBeDefined();
131+
expect(signedResult.messageHash).toBeDefined();
132+
expect(signedResult.rawTransaction).toBeDefined();
133+
expect(signedResult.transactionHash).toBeDefined();
134+
expect(signedResult.r).toMatch(/0[xX][0-9a-fA-F]{64}/);
135+
expect(signedResult.s).toMatch(/0[xX][0-9a-fA-F]{64}/);
136+
expect(signedResult.v).toMatch(/0[xX][0-9a-fA-F]+/);
137+
});
138+
139+
it.each(transactionsTestData)('Recover transaction', async txData => {
140+
const account = create();
141+
const txObj = { ...txData, from: account.address };
142+
const signedResult = await signTransaction(
143+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
144+
TransactionFactory.fromTxData(txObj),
145+
account.privateKey,
146+
);
147+
expect(signedResult).toBeDefined();
148+
149+
const address: Address = recoverTransaction(signedResult.rawTransaction);
150+
expect(address).toBeDefined();
151+
expect(address).toEqual(account.address);
152+
});
153+
});
154+
155+
describe('Hash Message', () => {
156+
it.each(validHashMessageData)('%s', (message, hash) => {
157+
expect(hashMessage(message)).toEqual(hash);
158+
});
159+
});
160+
161+
describe('Sign Message', () => {
162+
describe('sign', () => {
163+
it.each(signatureRecoverData)('%s', (data, testObj) => {
164+
const result = sign(data, testObj.privateKey);
165+
expect(result.signature).toEqual(testObj.signature || testObj.signatureOrV); // makes sure we get signature and not V value
166+
expect(result.r).toEqual(testObj.r);
167+
expect(result.s).toEqual(testObj.s);
168+
});
169+
});
170+
171+
describe('recover', () => {
172+
it.each(signatureRecoverData)('%s', (data, testObj) => {
173+
const address = recover(data, testObj.signatureOrV, testObj.prefixedOrR, testObj.s);
174+
expect(address).toEqual(testObj.address);
175+
});
176+
});
177+
});
178+
179+
describe('encrypt', () => {
180+
describe('valid cases', () => {
181+
it.each(validEncryptData)('%s', async (input, output) => {
182+
const result = await encrypt(input[0], input[1], input[2]).catch(err => {
183+
throw err;
184+
});
185+
expect(result.version).toBe(output.version);
186+
expect(result.address).toBe(output.address);
187+
expect(result.crypto.ciphertext).toBe(output.crypto.ciphertext);
188+
expect(result.crypto.cipherparams).toEqual(output.crypto.cipherparams);
189+
expect(result.crypto.cipher).toEqual(output.crypto.cipher);
190+
expect(result.crypto.kdf).toBe(output.crypto.kdf);
191+
expect(result.crypto.kdfparams).toEqual(output.crypto.kdfparams);
192+
expect(typeof result.version).toBe('number');
193+
expect(typeof result.id).toBe('string');
194+
expect(typeof result.crypto.mac).toBe('string');
195+
});
196+
});
197+
198+
describe('invalid cases', () => {
199+
it.each(invalidEncryptData)('%s', async (input, output) => {
200+
const result = encrypt(input[0], input[1], input[2]);
201+
await expect(result).rejects.toThrow(output);
202+
});
203+
});
204+
});
205+
206+
describe('decrypt', () => {
207+
describe('valid cases', () => {
208+
it.each(validDecryptData)('%s', async input => {
209+
const keystore = await encrypt(input[0], input[1], input[2]).catch(err => {
210+
throw err;
211+
});
212+
213+
// make sure decrypt does not throw invalid password error
214+
const result = await decrypt(keystore, input[1]);
215+
216+
expect(JSON.stringify(result)).toEqual(JSON.stringify(privateKeyToAccount(input[3])));
217+
218+
const keystoreString = JSON.stringify(keystore);
219+
220+
const stringResult = await decrypt(keystoreString, input[1], true);
221+
222+
expect(JSON.stringify(stringResult)).toEqual(JSON.stringify(privateKeyToAccount(input[3])));
223+
});
224+
});
225+
226+
describe('invalid cases', () => {
227+
it.each(invalidDecryptData)('%s', async (input, output) => {
228+
const result = decrypt(input[0], input[1]);
229+
230+
await expect(result).rejects.toThrow(output);
231+
});
232+
});
233+
234+
describe('invalid keystore, fails validation', () => {
235+
it.each(invalidKeyStore)('%s', async input => {
236+
const result = decrypt(input[0], input[1]);
237+
238+
await expect(result).rejects.toThrow(Web3ValidatorError);
239+
});
240+
});
241+
242+
describe('valid signatures for recover', () => {
243+
it.each(validRecover)('&s', (data, signature) => {
244+
recover(data, signature);
245+
});
246+
});
247+
});
248+
});

packages/web3-utils/src/uint8array.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ export function isUint8Array(data: unknown | Uint8Array): data is Uint8Array {
2121
(data as { constructor: { name: string } })?.constructor?.name === 'Uint8Array'
2222
);
2323
}
24+
25+
// @TODO: Remove this function and its usages once all sub dependencies uses version 1.3.3 or above of @noble/hashes
2426
export function ensureIfUint8Array<T = any>(data: T) {
2527
if (
2628
!(data instanceof Uint8Array) &&

0 commit comments

Comments
 (0)