diff --git a/scripts/helpers.js b/scripts/helpers.js index 2780c4edc..52102bdaf 100644 --- a/scripts/helpers.js +++ b/scripts/helpers.js @@ -1,31 +1,10 @@ -function chunk(array, size = 1) { - return Array.range(Math.ceil(array.length / size)).map(i => array.slice(i * size, i * size + size)); -} - -function range(start, stop = undefined, step = 1) { - if (!stop) { - stop = start; - start = 0; - } - return start < stop ? Array.from({ length: Math.ceil((stop - start) / step) }, (_, i) => start + i * step) : []; -} - -function unique(array, op = x => x) { - return array.filter((obj, i) => array.findIndex(entry => op(obj) === op(entry)) === i); -} - -function zip(...args) { - return Array.from({ length: Math.max(...args.map(arg => arg.length)) }, (_, i) => args.map(arg => arg[i])); -} - -function capitalize(str) { - return str.charAt(0).toUpperCase() + str.slice(1); -} +const iterate = require('../test/helpers/iterate'); module.exports = { - chunk, - range, - unique, - zip, - capitalize, + // Capitalize the first char of a string + // Example: capitalize('uint256') → 'Uint256' + capitalize: str => str.charAt(0).toUpperCase() + str.slice(1), + + // Iterate tools for the test helpers + ...iterate, }; diff --git a/test/helpers/governance.js b/test/helpers/governance.js index cc219323b..dce5927b7 100644 --- a/test/helpers/governance.js +++ b/test/helpers/governance.js @@ -175,7 +175,7 @@ class GovernorHelper { const statesCount = ethers.toBigInt(Object.keys(ProposalState).length); let result = 0n; - for (const state of unique(...proposalStates)) { + for (const state of unique(proposalStates)) { if (state < 0n || state >= statesCount) { expect.fail(`ProposalState ${state} out of possible states (0...${statesCount}-1)`); } else { diff --git a/test/helpers/iterate.js b/test/helpers/iterate.js index 220f2c368..ef4526e13 100644 --- a/test/helpers/iterate.js +++ b/test/helpers/iterate.js @@ -1,15 +1,36 @@ -// Map values in an object -const mapValues = (obj, fn) => Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, fn(v)])); - -// Cartesian product of a list of arrays -const product = (...arrays) => arrays.reduce((a, b) => a.flatMap(ai => b.map(bi => [...ai, bi])), [[]]); -const unique = (...array) => array.filter((obj, i) => array.indexOf(obj) === i); -const zip = (...args) => - Array.from({ length: Math.max(...args.map(array => array.length)) }, (_, i) => args.map(array => array[i])); - module.exports = { - mapValues, - product, - unique, - zip, + // ================================================= Array helpers ================================================= + + // Cut an array into an array of sized-length arrays + // Example: chunk([1,2,3,4,5,6,7,8], 3) → [[1,2,3],[4,5,6],[7,8]] + chunk: (array, size = 1) => + Array.from({ length: Math.ceil(array.length / size) }, (_, i) => array.slice(i * size, i * size + size)), + + // Cartesian cross product of an array of arrays + // Example: product([1,2],[a,b,c],[true]) → [[1,a,true],[1,b,true],[1,c,true],[2,a,true],[2,b,true],[2,c,true]] + product: (...arrays) => arrays.reduce((a, b) => a.flatMap(ai => b.map(bi => [...ai, bi])), [[]]), + + // Range from start to end in increment + // Example: range(17,42,7) → [17,24,31,38] + range: (start, stop = undefined, step = 1) => { + if (!stop) { + stop = start; + start = 0; + } + return start < stop ? Array.from({ length: Math.ceil((stop - start) / step) }, (_, i) => start + i * step) : []; + }, + + // Unique elements, with an optional getter function + // Example: unique([1,1,2,3,4,8,1,3,8,13,42]) → [1,2,3,4,8,13,42] + unique: (array, op = x => x) => array.filter((obj, i) => array.findIndex(entry => op(obj) === op(entry)) === i), + + // Zip arrays together. If some arrays are smaller, undefined is used as a filler. + // Example: zip([1,2],[a,b,c],[true]) → [[1,a,true],[2,b,undefined],[undefined,c,undefined]] + zip: (...args) => Array.from({ length: Math.max(...args.map(arg => arg.length)) }, (_, i) => args.map(arg => arg[i])), + + // ================================================ Object helpers ================================================= + + // Create a new object by mapping the values through a function, keeping the keys + // Example: mapValues({a:1,b:2,c:3}, x => x**2) → {a:1,b:4,c:9} + mapValues: (obj, fn) => Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, fn(v)])), }; diff --git a/test/utils/math/Math.test.js b/test/utils/math/Math.test.js index bce02610f..f38f2f318 100644 --- a/test/utils/math/Math.test.js +++ b/test/utils/math/Math.test.js @@ -6,8 +6,7 @@ const { PANIC_CODES } = require('@nomicfoundation/hardhat-chai-matchers/panic'); const { Rounding } = require('../../helpers/enums'); const { min, max, modExp } = require('../../helpers/math'); const { generators } = require('../../helpers/random'); -const { range } = require('../../../scripts/helpers'); -const { product } = require('../../helpers/iterate'); +const { product, range } = require('../../helpers/iterate'); const RoundingDown = [Rounding.Floor, Rounding.Trunc]; const RoundingUp = [Rounding.Ceil, Rounding.Expand]; diff --git a/test/utils/math/SafeCast.test.js b/test/utils/math/SafeCast.test.js index aa609faf0..ab62406ce 100644 --- a/test/utils/math/SafeCast.test.js +++ b/test/utils/math/SafeCast.test.js @@ -2,7 +2,7 @@ const { ethers } = require('hardhat'); const { expect } = require('chai'); const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); -const { range } = require('../../../scripts/helpers'); +const { range } = require('../../helpers/iterate'); async function fixture() { const mock = await ethers.deployContract('$SafeCast');