228 lines
5.6 KiB
JavaScript
228 lines
5.6 KiB
JavaScript
import { Option, Some, None } from '../src/option.js'
|
|
import { UnwrapError } from '../src/error.js'
|
|
import { Result } from '../src/result.js'
|
|
import { describe, it, mock } from 'node:test'
|
|
import assert from 'node:assert/strict'
|
|
|
|
const some = v => new Some(v)
|
|
const eq = assert.deepStrictEqual.bind(assert)
|
|
|
|
describe('Option', () => {
|
|
it('Option.some should return a Some', async () => {
|
|
eq(Option.some(1), new Some(1))
|
|
|
|
// fantasy-land/of
|
|
eq(Option['fantasy-land/of'](1), new Some(1))
|
|
})
|
|
|
|
it('Some.bind(fn) should call fn', async () => {
|
|
const a = some(1)
|
|
let res = a.bind(x => some(x * 2))
|
|
eq(res, some(2))
|
|
|
|
// fantasy-land/chain
|
|
res = a['fantasy-land/chain'](x => some(x * 2))
|
|
eq(res, some(2))
|
|
})
|
|
|
|
it('None.bind(fn) should not execute fn', async () => {
|
|
const fn = mock.fn(v => some(v * 2))
|
|
let res = None.bind(fn)
|
|
eq(res, None)
|
|
eq(fn.mock.callCount(), 0)
|
|
|
|
// fantasy-land/chain
|
|
res = None['fantasy-land/chain'](fn)
|
|
eq(res, None)
|
|
eq(fn.mock.callCount(), 0)
|
|
})
|
|
|
|
it('Some.map(fn) should transform the inner value', () => {
|
|
const a = some(1)
|
|
let res = a.map(x => x + 1)
|
|
eq(res, some(2))
|
|
|
|
// fantasy-land/map
|
|
res = a['fantasy-land/map'](x => x + 1)
|
|
eq(res, some(2))
|
|
})
|
|
|
|
it('None.map(fn) should not execute fn', async () => {
|
|
const fn = mock.fn(v => some(v * 2))
|
|
let res = None.map(fn)
|
|
eq(res, None)
|
|
eq(fn.mock.callCount(), 0)
|
|
|
|
// fantasy-land/chain
|
|
res = None['fantasy-land/map'](fn)
|
|
eq(res, None)
|
|
eq(fn.mock.callCount(), 0)
|
|
})
|
|
|
|
it('Some.and(other)', () => {
|
|
const a = some(1)
|
|
const b = some(2)
|
|
assert.strictEqual(a.and(b), b)
|
|
})
|
|
|
|
it('None.and(other)', () => {
|
|
assert.strictEqual(None.and(some(1)), None)
|
|
})
|
|
|
|
it('Some.alt(other) should return itself', () => {
|
|
const self = some(1)
|
|
const other = some(2)
|
|
let res = self.alt(other)
|
|
assert.strictEqual(res, self)
|
|
|
|
// fantasy-land/alt
|
|
res = self['fantasy-land/alt'](other)
|
|
assert.strictEqual(res, self)
|
|
})
|
|
|
|
it('None.alt(other) should return other', () => {
|
|
const other = some(2)
|
|
let res = None.alt(other)
|
|
assert.strictEqual(res, other)
|
|
|
|
// fantasy-land/alt
|
|
res = None['fantasy-land/alt'](other)
|
|
assert.strictEqual(res, other)
|
|
})
|
|
|
|
it('Option.orElse()', () => {
|
|
const nothing = mock.fn(() => None)
|
|
const chocolate = mock.fn(() => some('chocolate'))
|
|
eq(some('vanilla').orElse(chocolate), some('vanilla'))
|
|
eq(chocolate.mock.callCount(), 0)
|
|
eq(None.orElse(chocolate), some('chocolate'))
|
|
eq(chocolate.mock.callCount(), 1)
|
|
eq(None.orElse(nothing), None)
|
|
eq(nothing.mock.callCount(), 1)
|
|
})
|
|
|
|
it('Some.filter(predicate)', () => {
|
|
const a = some(1)
|
|
assert.strictEqual(a.filter(x => x == 1), a)
|
|
assert.strictEqual(a.filter(x => x == 2), None)
|
|
|
|
// fantasy-land/filter
|
|
assert.strictEqual(a['fantasy-land/filter'](x => x == 1), a)
|
|
})
|
|
|
|
it('None.filter(predicate)', () => {
|
|
assert.strictEqual(None.filter(x => x == 1), None)
|
|
|
|
// fantasy-land/filter
|
|
assert.strictEqual(None['fantasy-land/filter'](x => x == 1), None)
|
|
})
|
|
|
|
it('Some.okOr(err)', () => {
|
|
eq(some(1).okOr(2), Result.ok(1))
|
|
})
|
|
|
|
it('None.okOr(err)', () => {
|
|
eq(None.okOr(2), Result.err(2))
|
|
})
|
|
|
|
it('Some.okOrElse(err)', () => {
|
|
eq(some(1).okOrElse(() => 2), Result.ok(1))
|
|
})
|
|
|
|
it('None.okOr(err)', () => {
|
|
eq(None.okOrElse(() => 2), Result.err(2))
|
|
})
|
|
|
|
it('Some.equals(other)', () => {
|
|
const a = some(1)
|
|
|
|
assert.ok(a.equals(some(1)))
|
|
assert(!a.equals(some(2)))
|
|
assert(!a.equals(None))
|
|
|
|
// fantasy-land/equals
|
|
assert.ok(a['fantasy-land/equals'](some(1)))
|
|
})
|
|
|
|
it('None.equals(other)', () => {
|
|
assert.ok(None.equals(None))
|
|
assert(!None.equals(some(1)))
|
|
|
|
// fantasy-land/equals
|
|
assert.ok(None['fantasy-land/equals'](None))
|
|
})
|
|
|
|
it('Some.lte(other)', () => {
|
|
const a = some(1)
|
|
|
|
assert.ok(a.lte(some(1)))
|
|
assert.ok(a.lte(some(2)))
|
|
assert.ok(!a.lte(some(0)))
|
|
assert(!a.lte(None))
|
|
|
|
// fantasy-land/lte
|
|
assert.ok(a['fantasy-land/lte'](some(1)))
|
|
})
|
|
|
|
it('None.lte(other)', () => {
|
|
assert(!None.lte(None))
|
|
assert(!None.lte(some(1)))
|
|
|
|
// fantasy-land/lte
|
|
assert.ok(!None['fantasy-land/lte'](None))
|
|
})
|
|
|
|
it('Some.flatten()', async () => {
|
|
eq(some(1).flatten(), some(1))
|
|
eq(some(some(1)).flatten(), some(1))
|
|
eq(some(some(some(1))).flatten(), some(some(1)))
|
|
eq(some(None).flatten(), None)
|
|
})
|
|
|
|
it('None.flatten()', async () => {
|
|
eq(None.flatten(), None)
|
|
})
|
|
|
|
it('Some.inspect(fn) should call fn', async () => {
|
|
const fn = mock.fn(_x => { })
|
|
some(1).inspect(fn)
|
|
eq(fn.mock.callCount(), 1)
|
|
})
|
|
|
|
it('None.inspect(fn) should not call fn', async () => {
|
|
const fn = mock.fn(_x => { })
|
|
None.inspect(fn)
|
|
eq(fn.mock.callCount(), 0)
|
|
})
|
|
|
|
it('Some.unwrap() returns inner value', async () => {
|
|
const a = some(1)
|
|
assert.doesNotThrow(a.unwrap.bind(a), UnwrapError)
|
|
eq(a.unwrap(), 1)
|
|
})
|
|
|
|
it('None.unwrap() throws UnwrapError', async () => {
|
|
assert.throws(None.unwrap, UnwrapError)
|
|
})
|
|
|
|
it('Some.unwrapOr(value) returns inner value', async () => {
|
|
eq(some(1).unwrapOr(2), 1)
|
|
})
|
|
|
|
it('None.unwrapOr(value) returns value', async () => {
|
|
eq(None.unwrapOr(2), 2)
|
|
})
|
|
|
|
it('Some.unwrapOrElse(fn) returns inner value', async () => {
|
|
const fn = mock.fn(() => 2)
|
|
const a = some(1)
|
|
eq(a.unwrapOrElse(fn), 1)
|
|
eq(fn.mock.callCount(), 0)
|
|
})
|
|
|
|
it('None.unwrapOrElse(fn) returns result of fn', async () => {
|
|
const fn = mock.fn(() => 2)
|
|
eq(None.unwrapOrElse(fn), 2)
|
|
eq(fn.mock.callCount(), 1)
|
|
})
|
|
})
|