diff --git a/src/check/arbitrary/ConstantArbitrary.ts b/src/check/arbitrary/ConstantArbitrary.ts index 1f2a704ffa1..f37f1ed76b8 100644 --- a/src/check/arbitrary/ConstantArbitrary.ts +++ b/src/check/arbitrary/ConstantArbitrary.ts @@ -3,6 +3,7 @@ import { stream } from '../../stream/Stream'; import { cloneMethod, hasCloneMethod } from '../symbols'; import { Arbitrary } from './definition/Arbitrary'; import { Shrinkable } from './definition/Shrinkable'; +import { findOrUndefined } from './helpers/ArrayHelper'; /** @hidden */ class ConstantArbitrary extends Arbitrary { @@ -56,7 +57,7 @@ function constantFrom(...values: T[]): Arbitrary { if (values.length === 0) { throw new Error('fc.constantFrom expects at least one parameter'); } - if (values.find(v => hasCloneMethod(v)) != null) { + if (findOrUndefined(values, v => hasCloneMethod(v)) != undefined) { throw new Error('fc.constantFrom does not accept cloneable values, not supported for the moment'); } return new ConstantArbitrary([...values]); diff --git a/src/check/arbitrary/helpers/ArrayHelper.ts b/src/check/arbitrary/helpers/ArrayHelper.ts new file mode 100644 index 00000000000..7a88dbe86e1 --- /dev/null +++ b/src/check/arbitrary/helpers/ArrayHelper.ts @@ -0,0 +1,12 @@ +/** + * @hidden + * Find first element matching the predicate in the array, or null if none match + * Equivalent to Array.prototype.find, but works on Internet Explorer 11. + */ +export function findOrUndefined(ts: ArrayLike, f: (t: T) => boolean): T | undefined { + for (let i = 0; i < ts.length; i++) { + const t = ts[i]; + if (f(t)) return t; + } + return undefined; +} diff --git a/test/unit/check/arbitrary/helpers/ArrayHelper.spec.ts b/test/unit/check/arbitrary/helpers/ArrayHelper.spec.ts new file mode 100644 index 00000000000..1fcc189a799 --- /dev/null +++ b/test/unit/check/arbitrary/helpers/ArrayHelper.spec.ts @@ -0,0 +1,46 @@ +import * as fc from '../../../../../lib/fast-check'; +import { findOrUndefined } from '../../../../../src/check/arbitrary/helpers/ArrayHelper'; + +describe('ArrayHelper', () => { + describe('findOrUndefined', () => { + it('should return undefined for empty array', () => { + expect( + findOrUndefined([], () => { + throw new Error('⊥'); + }) + ).toBe(undefined); + }); + it('should return a matching element', () => { + fc.assert( + fc.property(fc.array(fc.integer()), fc.integer(), fc.array(fc.integer()), (prefix, e, suffix) => { + expect(findOrUndefined([...prefix, e, ...suffix], x => x === e)).toBe(e); + }) + ); + }); + it('should return undefined when not present', () => { + fc.assert( + fc.property( + fc.array(fc.integer(0, 100)), + fc.integer(-100, -1), + fc.array(fc.integer(0, 100)), + (prefix, e, suffix) => { + expect(findOrUndefined([...prefix, ...suffix], x => x === e)).toBe(undefined); + } + ) + ); + }); + it('should pass some simple examples', () => { + expect(findOrUndefined([7, 8, 2, 1], x => x < 3)).toBe(2); + expect(findOrUndefined([12, 8, -1], x => x < 3)).toBe(-1); + expect(findOrUndefined([3], x => x > 10)).toBe(undefined); + expect(findOrUndefined([], x => x > 10)).toBe(undefined); + }); + it('should be consistent with Array.prototype.indexOf', () => { + fc.assert( + fc.property(fc.array(fc.integer(1, 200)), fc.integer(1, 200), (xs, x) => { + expect(findOrUndefined(xs, a => a === x) === undefined).toBe(xs.indexOf(x) === -1); + }) + ); + }); + }); +});