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) }) })