Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow overrides to be used for unknown types #1

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 6 additions & 14 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@
"name": "fast-check-io-ts",
"version": "0.4.0",
"description": "io-ts codec to fast-check arbitrary mapping",
"files": [
"lib"
],
"files": ["lib"],
"main": "lib/index.js",
"typings": "lib/index.d.ts",
"repository": {
Expand All @@ -27,14 +25,7 @@
"lint": "tslint -p tsconfig.json src/*.ts",
"dtslint": "dtslint dtslint"
},
"keywords": [
"fast-check",
"io-ts",
"typescript",
"arbitrary",
"generative",
"testing"
],
"keywords": ["fast-check", "io-ts", "typescript", "arbitrary", "generative", "testing"],
"author": "Giovanni Gonzaga <[email protected]>",
"license": "MIT",
"devDependencies": {
Expand All @@ -49,12 +40,13 @@
"rimraf": "^2.6.3",
"ts-jest": "^24.0.2",
"tslint": "^5.18.0",
"typescript": "^3.5.3"
"typescript": "^3.5.3",
"io-ts-types": "^0.5.6"
},
"peerDependencies": {
"fast-check": "^1.16.0",
"io-ts": "^2.0.0",
"fp-ts": "^2.0.0"
"fp-ts": "^2.0.0",
"io-ts": "^2.0.0"
},
"jest": {
"preset": "ts-jest"
Expand Down
27 changes: 20 additions & 7 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ export type HasArbitrary =
| IntersectionType
| BrandedType;

interface Overrides {
[key: string]: fc.Arbitrary<any>;
}

function getProps(codec: t.InterfaceType<any> | t.ExactType<any> | t.PartialType<any>): t.Props {
switch (codec._tag) {
case 'InterfaceType':
Expand All @@ -44,11 +48,11 @@ function getProps(codec: t.InterfaceType<any> | t.ExactType<any> | t.PartialType

const objectTypes = ['ExactType', 'InterfaceType', 'PartialType'];

export function getArbitrary<T extends HasArbitrary>(codec: T): fc.Arbitrary<t.TypeOf<T>> {
export function getArbitrary<T extends HasArbitrary>(codec: T, overrides: Overrides = {}): fc.Arbitrary<t.TypeOf<T>> {
const type: HasArbitrary = codec as any;
switch (type._tag) {
case 'UnknownType':
return fc.anything();
return fc.anything() as any;
case 'UndefinedType':
case 'VoidType':
return fc.constant(undefined) as any;
Expand All @@ -71,19 +75,28 @@ export function getArbitrary<T extends HasArbitrary>(codec: T): fc.Arbitrary<t.T
case 'InterfaceType':
case 'PartialType':
case 'ExactType':
return fc.record(record.map(getProps(type), getArbitrary as any) as any) as any;
return fc.record(record.map(getProps(type), codec => getArbitrary(codec as any, overrides)) as any) as any;
case 'TupleType':
return (fc.tuple as any)(...type.types.map(getArbitrary));
return (fc.tuple as any)(...type.types.map(codec => getArbitrary(codec, overrides)));
case 'UnionType':
return fc.oneof(...type.types.map(getArbitrary)) as any;
return fc.oneof(...type.types.map(codec => getArbitrary(codec, overrides))) as any;
case 'IntersectionType':
const isObjectIntersection = objectTypes.includes(type.types[0]._tag);
return isObjectIntersection
? (fc.tuple as any)(...type.types.map(t => getArbitrary(t)))
? (fc.tuple as any)(...type.types.map(t => getArbitrary(t, overrides)))
.map((values: Array<object>) => Object.assign({}, ...values))
.filter(type.is)
: fc.oneof(...type.types.map(t => getArbitrary(t))).filter(type.is);
: fc.oneof(...type.types.map(t => getArbitrary(t, overrides))).filter(type.is);
case 'RefinementType':
return getArbitrary(type.type).filter(type.predicate) as any;
default:
// if we cannot find the type, check whether it has been passed as an
// override and use that instead
const typeName = (type as any).name;
if (typeName && overrides[typeName] !== undefined) {
return overrides[typeName];
}
// failing that, throw an error?
throw Error(`Could not create an Arbitrary for ${typeName}. Consider passing in a custom override?`);
}
}
7 changes: 6 additions & 1 deletion test/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import * as fc from 'fast-check';
import * as t from 'io-ts';
import { getArbitrary, HasArbitrary } from '../src';
import { date } from 'io-ts-types/lib/date';

// pass
const overrides = { Date: fc.date() };

function test<T extends HasArbitrary>(codec: T): void {
it(codec.name, () => {
fc.assert(fc.property(getArbitrary(codec), codec.is));
fc.assert(fc.property(getArbitrary(codec, overrides), codec.is));
});
}

Expand All @@ -27,3 +31,4 @@ test(t.intersection([t.type({ foo: t.string }), t.partial({ bar: t.number })]));
test(t.intersection([t.type({ foo: t.string }), t.type({ bar: t.number })]));
test(t.intersection([t.array(t.string), t.array(t.number)]));
test(t.record(t.string, t.number));
test(t.type({ date }));