UNPKG

fast-check

Version:

Property based testing framework for JavaScript (like QuickCheck)

116 lines (115 loc) 6.21 kB
import { stringify } from '../../utils/stringify.js'; import { array } from './ArrayArbitrary.js'; import { boolean } from './BooleanArbitrary.js'; import { constant } from './ConstantArbitrary.js'; import { dictionary, toObject } from './DictionaryArbitrary.js'; import { double } from './FloatingPointArbitrary.js'; import { frequency } from './FrequencyArbitrary.js'; import { integer } from './IntegerArbitrary.js'; import { memo } from './MemoArbitrary.js'; import { oneof } from './OneOfArbitrary.js'; import { set } from './SetArbitrary.js'; import { string, unicodeString } from './StringArbitrary.js'; import { tuple } from './TupleArbitrary.js'; import { bigInt } from './BigIntArbitrary.js'; class QualifiedObjectConstraints { constructor(key, values, maxDepth, maxKeys, withSet, withMap, withObjectString, withNullPrototype, withBigInt) { this.key = key; this.values = values; this.maxDepth = maxDepth; this.maxKeys = maxKeys; this.withSet = withSet; this.withMap = withMap; this.withObjectString = withObjectString; this.withNullPrototype = withNullPrototype; this.withBigInt = withBigInt; } static defaultValues() { return [ boolean(), integer(), double(), string(), oneof(string(), constant(null), constant(undefined)), oneof(double(), constant(-0), constant(0), constant(Number.NaN), constant(Number.POSITIVE_INFINITY), constant(Number.NEGATIVE_INFINITY), constant(Number.EPSILON), constant(Number.MIN_VALUE), constant(Number.MAX_VALUE), constant(Number.MIN_SAFE_INTEGER), constant(Number.MAX_SAFE_INTEGER)), ]; } static boxArbitraries(arbs) { return arbs.map((arb) => arb.map((v) => { switch (typeof v) { case 'boolean': return new Boolean(v); case 'number': return new Number(v); case 'string': return new String(v); default: return v; } })); } static boxArbitrariesIfNeeded(arbs, boxEnabled) { return boxEnabled ? QualifiedObjectConstraints.boxArbitraries(arbs).concat(arbs) : arbs; } static from(settings) { function getOr(access, value) { return settings != null && access() != null ? access() : value; } return new QualifiedObjectConstraints(getOr(() => settings.key, string()), QualifiedObjectConstraints.boxArbitrariesIfNeeded(getOr(() => settings.values, QualifiedObjectConstraints.defaultValues()), getOr(() => settings.withBoxedValues, false)), getOr(() => settings.maxDepth, 2), getOr(() => settings.maxKeys, 5), getOr(() => settings.withSet, false), getOr(() => settings.withMap, false), getOr(() => settings.withObjectString, false), getOr(() => settings.withNullPrototype, false), getOr(() => settings.withBigInt, false)); } } const anythingInternal = (constraints) => { const arbKeys = constraints.withObjectString ? memo((n) => frequency({ arbitrary: constraints.key, weight: 10 }, { arbitrary: anythingArb(n).map((o) => stringify(o)), weight: 1 })) : memo(() => constraints.key); const arbitrariesForBase = constraints.values; const maxDepth = constraints.maxDepth; const maxKeys = constraints.maxKeys; const entriesOf = (keyArb, valueArb) => set(tuple(keyArb, valueArb), 0, maxKeys, (t1, t2) => t1[0] === t2[0]); const mapOf = (ka, va) => entriesOf(ka, va).map((v) => new Map(v)); const dictOf = (ka, va) => entriesOf(ka, va).map((v) => toObject(v)); const baseArb = oneof(...arbitrariesForBase); const arrayBaseArb = oneof(...arbitrariesForBase.map((arb) => array(arb, 0, maxKeys))); const objectBaseArb = (n) => oneof(...arbitrariesForBase.map((arb) => dictOf(arbKeys(n), arb))); const setBaseArb = () => oneof(...arbitrariesForBase.map((arb) => set(arb, 0, maxKeys).map((v) => new Set(v)))); const mapBaseArb = (n) => oneof(...arbitrariesForBase.map((arb) => mapOf(arbKeys(n), arb))); const arrayArb = memo((n) => oneof(arrayBaseArb, array(anythingArb(n), 0, maxKeys))); const setArb = memo((n) => oneof(setBaseArb(), set(anythingArb(n), 0, maxKeys).map((v) => new Set(v)))); const mapArb = memo((n) => oneof(mapBaseArb(n), oneof(mapOf(arbKeys(n), anythingArb(n)), mapOf(anythingArb(n), anythingArb(n))))); const objectArb = memo((n) => oneof(objectBaseArb(n), dictOf(arbKeys(n), anythingArb(n)))); const anythingArb = memo((n) => { if (n <= 0) return oneof(baseArb); return oneof(baseArb, arrayArb(), objectArb(), ...(constraints.withMap ? [mapArb()] : []), ...(constraints.withSet ? [setArb()] : []), ...(constraints.withObjectString ? [anythingArb().map((o) => stringify(o))] : []), ...(constraints.withNullPrototype ? [objectArb().map((o) => Object.assign(Object.create(null), o))] : []), ...(constraints.withBigInt ? [bigInt()] : [])); }); return anythingArb(maxDepth); }; const objectInternal = (constraints) => { return dictionary(constraints.key, anythingInternal(constraints)); }; function anything(constraints) { return anythingInternal(QualifiedObjectConstraints.from(constraints)); } function object(constraints) { return objectInternal(QualifiedObjectConstraints.from(constraints)); } function jsonSettings(stringArbitrary, maxDepth) { const key = stringArbitrary; const values = [boolean(), integer(), double(), stringArbitrary, constant(null)]; return maxDepth != null ? { key, values, maxDepth } : { key, values }; } function jsonObject(maxDepth) { return anything(jsonSettings(string(), maxDepth)); } function unicodeJsonObject(maxDepth) { return anything(jsonSettings(unicodeString(), maxDepth)); } function json(maxDepth) { const arb = maxDepth != null ? jsonObject(maxDepth) : jsonObject(); return arb.map(JSON.stringify); } function unicodeJson(maxDepth) { const arb = maxDepth != null ? unicodeJsonObject(maxDepth) : unicodeJsonObject(); return arb.map(JSON.stringify); } export { anything, object, jsonObject, unicodeJsonObject, json, unicodeJson };