describe("associativity", function () { jsc.property("xs ++ (ys ++ zs) ≡ (xs ++ ys) ++ zs", "array nat", "array nat", "array nat", function (xs, ys, zs) { xs = lazyseq.fromArray(xs); ys = lazyseq.fromArray(ys); zs = lazyseq.fromArray(zs); var lhs = lazyseq.append(xs, lazyseq.append(ys, zs)); var rhs = lazyseq.append(lazyseq.append(xs, ys), zs); return _.isEqual(lhs.toArray(), rhs.toArray()); }); jsc.property("xs ++ (ys ++ zs) ≡ xs ++ ys ++ zs", "array nat", "array nat", "array nat", function (xs, ys, zs) { xs = lazyseq.fromArray(xs); ys = lazyseq.fromArray(ys); zs = lazyseq.fromArray(zs); var lhs = lazyseq.append(xs, lazyseq.append(ys, zs)); var rhs = lazyseq.append(xs, ys, zs); return _.isEqual(lhs.toArray(), rhs.toArray()); }); jsc.property("kind of flatten", "array (array nat)", function (xs) { var ys = xs.map(lazyseq.fromArray); var lhs = lazyseq.append.apply(undefined, ys).toArray(); var rhs = _.flatten(xs); return _.isEqual(lhs, rhs); }); });
describe('Equality', () => { const { A, B } = union('AB', { A: (value) => ({ value }), B: (value) => ({ value }) }).derive(equality) property('Different simple values are NOT equal', 'json', (a) => { return !A(a).equals(B(a)) }); property('Different composite values are NOT equal', 'json', (a) => { return !A(B(a)).equals(A(a)) }); property('Similar simple values are equal', 'json', (a) => { return A(a).equals(A(a)) }); property('Similar composite values are equal', 'json', (a) => { return A(B(a)).equals(A(B(a))) }); describe('Equality#withCustomComparison', () => { const { A } = union('A', { A: (value) => ({ value }), }).derive(equality.withCustomComparison((a, b) => a.id === b.id)); property('Values are compared using a custom function if provided', 'json', 'json', function(a, b) { return A({id:1, _irrelevantValue:a}).equals(A({id:1, _irrelevantValue: b})) }); }); });
describe('arbitrary selector', function() { jsc.property('div(".class") ≡ React.createElement("div.class")', "nestring", function(className) { const selector = '.' + className; const hr = React.createElement('div' + selector); const divr = div(selector); return jsc.utils.isApproxEqual(hr, divr); }); jsc.property('div("#id") ≡ React.createElement("div#id")', "nestring", function(id) { const selector = '#' + id; const hr = React.createElement('div' + selector); const divr = div(selector); return jsc.utils.isApproxEqual(hr, divr); }); jsc.property('div("[.#]foo", children) ≡ React.createElement("div[.#]id", children)', selChars, "nestring", "array string", function(selChar, id, children) { const selector = selChar + id; const hr = React.createElement('div' + selector, children); const divr = div(selector, children); return jsc.utils.isApproxEqual(hr, divr); }); jsc.property('div("[.#]foo", attrs, children) ≡ React.createElement("div[.#]id", attrs, children)', selChars, "nestring", "dict string", "array string", function(selChar, id, attrs, children) { const selector = selChar + id; const hr = React.createElement('div' + selector, attrs, children); const divr = div(selector, attrs, children); return jsc.utils.isApproxEqual(hr, divr); }); });
describe("right identities", function () { jsc.property("nil", "array nat", function (arr) { var lhs = lazyseq.append(arr, lazyseq.nil).toArray(); var rhs = arr; return _.isEqual(lhs, rhs); }); jsc.property("[]", "array nat", function (arr) { var lhs = lazyseq.append(arr, []).toArray(); var rhs = arr; return _.isEqual(lhs, rhs); }); jsc.property("() → nil", "array nat", function (arr) { var lhs = lazyseq.append(arr, function () { return lazyseq.nil; }).toArray(); var rhs = arr; return _.isEqual(lhs, rhs); }); jsc.property("() → []", "array nat", function (arr) { var lhs = lazyseq.append(arr, function () { return []; }).toArray(); var rhs = arr; return _.isEqual(lhs, rhs); }); });
describe('Serialization', () => { const AB = union('folktale:AB', { A: (value) => ({ value }), B: (value) => ({ value }) }).derive(serialization, equality); const CD = union('folktale:CD', { C: (value) => ({value}), D: (value) => ({value}) }).derive(serialization, equality); const {A, B} = AB; const {C, D} = CD; property('Serializing a value and deserializing it yields a similar value', 'json', (a) => { return AB.fromJSON(A(a).toJSON()).equals(A(a)) }) property('Serializing a *recursive* value and deserializing it yields a similar value', 'json', (a) => { return AB.fromJSON(A(B(a)).toJSON()).equals(A(B(a))) }) property('Serializing a *composite* value and deserializing it yields a similar value (when the proper parsers are provided).', 'json', (a) => { return AB.fromJSON(A(B(C(a))).toJSON(), {AB, CD}).equals(A(B(C(a)))) }) });
describe('curry properties', function() { jsv.property('curries multiple values', funcN(4), jsv.json, jsv.json, jsv.json, jsv.json, function(f, a, b, c, d) { var g = R.curry(f); return R.all(R.equals(f(a, b, c, d)), [ g(a, b, c, d), g(a)(b)(c)(d), g(a)(b, c, d), g(a, b)(c, d), g(a, b, c)(d) ]); }); jsv.property('curries with placeholder', funcN(3), jsv.json, jsv.json, jsv.json, function(f, a, b, c) { var _ = {'@@functional/placeholder': true, x: Math.random()}; var g = R.curry(f); return R.all(R.equals(f(a, b, c)), [ g(_, _, c)(a, b), g(a, _, c)(b), g(_, b, c)(a), g(a, _, _)(_, c)(b), g(a, b, _)(c) ]); }); });
describe('Conversions', () => { property('Just#fromResult', 'json', (a) => { return Maybe.fromResult(Just(a).toResult()).equals(Just(a)); }); property('Nothing#fromResult', 'json', (a) => { return Maybe.fromResult(Nothing().toResult()).equals(Nothing()); }); });
describe('minimumOf', () => { jsv.property('minimumOf empty', () => R.equals(Fold.minimumOf(Traversal.traversed, []), RF.Maybe.Nothing())); jsv.property('minimumOf non-empty', jsv.nearray(jsv.nat), xs => R.equals(Fold.minimumOf(Traversal.traversed, xs), RF.Maybe.Just(R.reduce(R.min, Infinity, xs)))); });
describe('lastOf', () => { jsv.property('lastOf empty', () => R.equals(Fold.lastOf(Traversal.traversed, []), RF.Maybe.Nothing())); jsv.property('lastOf non-empty', jsv.nearray(jsv.nat), xs => R.equals(Fold.lastOf(Traversal.traversed, xs), RF.Maybe.Just(R.last(xs)))); });
describe("partitions", function () { it("example 1", function () { var result = utils.partitions(3, 1); chai.expect(result).to.deep.equal([[3]]); }); it("example 2", function () { var result = utils.partitions(3, 2); chai.expect(normalize(result)).to.deep.equal(normalize([ [3, 0], [2, 1], [1, 2], [0, 3], ])); }); it("example 3", function () { var result = utils.partitions(3, 3); chai.expect(normalize(result)).to.deep.equal(normalize([ [0, 0, 3], [0, 1, 2], [0, 2, 1], [0, 3, 0], [1, 0, 2], [1, 1, 1], [1, 2, 0], [2, 0, 1], [2, 1, 0], [3, 0, 0], ])); }); var genN = "nat 7"; var genM = "nat 6"; jsc.property("partitions are of right size", genN, genM, function (n, m) { m += 1; var result = utils.partitions(n, m); return result.every(function (p) { return p.length === m; }); }); jsc.property("partitions add to size", genN, genM, function (n, m) { m += 1; var result = utils.partitions(n, m); return result.every(function (p) { return n === p.reduce(function (a, b) { return a + b; }, 0); }); }); jsc.property("there are all partitions", genN, genM, function (n, m) { m += 1; // console.log(n, m, binomialCoefficient(m + n - 1, n)); var result = utils.partitions(n, m); return result.length === binomialCoefficient(m + n - 1, n); }); });
describe('notElemOf', () => { jsv.property('notElemOf', jsv.json, jsv.array(jsv.json), (x, xs) => R.equals(Fold.notElemOf(Traversal.traversed, x, xs), !R.contains(x, xs))); jsv.property('notElemOf does not exist', jsv.json, jsv.nearray(jsv.json), (x, xs) => Fold.notElemOf(Traversal.traversed, x, R.without([x], xs))); });
describe('elemOf', () => { jsv.property('elemOf', jsv.json, jsv.array(jsv.json), (x, xs) => R.equals(Fold.elemOf(Traversal.traversed, x, xs), R.contains(x, xs))); jsv.property('elemOf does exist', jsv.nearray(jsv.json), xs => Fold.elemOf(Traversal.traversed, xs[0], xs)); });
describe('Monoid instance', _ => { property('right identity', 'string', (a) => equals(type(a)[concat](type()[empty]()), type(a)) ); property('left identity', 'string', (a) => equals(type()[empty]()[concat](type(a)), type(a)) ); });
describe("tail", function () { jsc.property("nil.tail() === nil", function () { return lazyseq.nil.tail() === lazyseq.nil; }); jsc.property("cons(n, nil).tail() === nil", "nat", function (n) { return lazyseq.cons(n, lazyseq.nil).tail() === lazyseq.nil; }); });
describe('converge', () => { jsc.property('converges results of individual functions', 'fun', 'array fun', 'nearray', (fn, fns, args) => converge(fn, fns, ...args) === fn(...fns.map(f => f(...args))) ) jsc.property('curry', 'fun', 'array fun', 'nearray', (fn, fns, args) => converge(fn)(fns)(...args) === converge(fn, fns, ...args) ) })
describe('#getOrElse(b)', () => { property('Just(a).getOrElse(b) = a', 'json', 'json', (a, b) => { return Just(a).getOrElse(b) === a; }); property('Nothing().getOrElse(b) = b', 'json', (a) => { return Nothing().getOrElse(a) === a; }); });
describe('#empty()', () => { property('empty().equals(Nothing()) = true', () => { return Maybe.empty().equals(Nothing()); }); property('!(empty().equals(Just(a))) = false', 'nat', (a) => { return !(Maybe.empty().equals(Just(a))); }); });
describe('#chain(f)', () => { property('Just(a).chain(f) = f(a)', 'json', 'json -> json', (a, f) => { return Just(a).chain(x => Just(f(x))).equals(Just(f(a))); }); property('Nothing().chain(f) = Nothing()', 'json -> json', (f) => { return Nothing().chain(x => Just(f(x))).equals(Nothing()); }); });
describe('#orElse(f)', () => { property('Just(a).orElse(f) = Just(a)', 'json', 'json -> json', (a, f) => { return Just(a).orElse(x => Just(f(x))).equals(Just(a)); }); property('Nothing().orElse(f) = f()', 'json', 'json -> json', (a, f) => { return Nothing().orElse(x => Just(f(a))).equals(Just(f(a))); }); });
describe('#alt(f)', () => { property('Nothing().alt(Just(a)).alt(Just(b)) = Nothing().alt(Just(a).alt(Just(b)))', 'nat', 'nat', (a, b) => { return Nothing().alt(Just(a)).alt(Just(b)).equals(Nothing().alt(Just(a).alt(Just(b)))); }); property('Just(a).alt(Just(b)).map(f) = Just(a).map(f).alt(Just(b).map(f))', 'nat', 'nat', (a, b) => { const f = x => x + 1; return Just(a).alt(Just(b)).map(f).equals(Just(a).map(f).alt(Just(b).map(f))); }); });
describe('Profunctor laws', () => { jsv.property('identity', p, p => eq(p.dimap(R.identity, R.identity), p)); jsv.property('composition', natFn, natFn, natFn, natFn, p, (f, f_, h, h_, p) => eq(p.dimap(R.compose(h, h_), R.compose(f_, f)), p.dimap(h, f).dimap(h_, f_))); });
describe('sequenceOf_', () => { jsv.property('sequenceOf_ all Just', jsv.array(JustArb(jsv.json)), xs => R.equals(Fold.sequenceOf_(RF.Maybe, Traversal.traversed, xs), RF.Maybe.Just({}))); jsv.property('sequenceOf_ with Nothing', jsv.array(MaybeArb(jsv.json)), xs => R.equals(Fold.sequenceOf_(RF.Maybe, Traversal.traversed, R.prepend(RF.Maybe.Nothing(), xs)), RF.Maybe.Nothing())); });
describe('#toValidation(b)', () => { const { Success, Failure } = require('folktale/validation'); property('Just(a).toValidation(b) = Succcess(a)', 'json', 'json', (a, b) => { return Just(a).toValidation(b).equals(Success(a)); }); property('Nothing().toValidation(b) = Failure(b)', 'json', 'json', (a, b) => { return Nothing().toValidation(b).equals(Failure(b)); }); });
describe('#toResult(b)', () => { const { Error, Ok } = require('folktale/result'); property('Just(a).toResult(b) = Right(a)', 'json', 'json', (a, b) => { return Just(a).toResult(b).equals(Ok(a)); }); property('Nothing().toResult(b) = Error(b)', 'json', 'json', (a, b) => { return Nothing().toResult(b).equals(Error(b)); }); });
describe('Functor instance', _ => { property('identity', 'nat', (a) => equals(type(a)[map](a => a), type(a)) ); property('composition', 'nat', 'nat -> nat', 'nat -> nat', (a, f, g) => equals( type(a)[map](x => f(g(x))), type(a)[map](g)[map](f) ) ); });
describe('toListOf', () => { jsv.property('toListOf list', jsv.array(jsv.json), xs => R.equals(Fold.toListOf(Traversal.traversed, xs), xs)); jsv.property('toListOf Just', jsv.json, a => R.equals(Fold.toListOf(Prism._Just, RF.Maybe.Just(a)), [a])); jsv.property('toListOf Nothing', () => R.equals(Fold.toListOf(Prism._Just, RF.Maybe.Nothing()), [])); });
describe('#map', () => { jsv.property('identity law', 'array unit', xs => R.equals(xs, map(x => x, xs)) ); jsv.property('composition law', 'array nat', 'nat', (xs, x) => { const f = R.curry((a, b) => a + b); const left = map(y => f(x)(y), xs); const right = map(g => g(x), map(f, xs)); return R.equals(left, right); }); });
describe('#apply(a)', () => { property('Just(f).apply(a) = Just(f(a))', 'nat', 'nat -> nat', (a, f) => { return Just(f).apply(Just(a)).equals(Just(f(a))); }); property('Just(f).apply(Nothing()) = Nothing()', 'nat -> nat', (f) => { return Just(f).apply(Nothing()).equals(Nothing()); }) property('Nothing().apply(a) = Nothing()', 'nat', (a) => { return Nothing().apply(Just(a)).equals(Nothing()); }); });
describe('#concat(a)', () => { property('Just(a).concat(Nothing()) = Just(a)', 'string', (a) => { return Just(a).concat(Maybe.empty()).equals(Just(a)); }); property('Nothing().concat(Just(a)) = Nothing()', 'string', (a) => { return Maybe.empty().concat(Just(a)).equals(Just(a)); }); property('Just(a).concat(Just(b)) = Just(c)', 'string', 'string', (a, b) => { return Just(a).concat(Just(b)).equals(Just(a.concat(b))); }); });
describe('#map', () => { jsv.property('identity law', 'ZipArray unit', env, xs => R.equals(xs.runZipArray, xs.map(x => x).runZipArray) ); jsv.property('composition law', 'ZipArray nat', 'nat', env, (xs, x) => { const f = R.curry((a, b) => a + b); const left = xs.map(y => f(x)(y)).runZipArray; const right = xs.map(f).map(f => f(x)).runZipArray; return R.equals(left, right); }); });