fast-check
Version:
Property based testing framework for JavaScript (like QuickCheck)
116 lines (115 loc) • 6.21 kB
JavaScript
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 };