Skip to content

Commit c1e5d52

Browse files
Option | Improve tests and docs (#3)
1 parent 2593f13 commit c1e5d52

File tree

8 files changed

+476
-51
lines changed

8 files changed

+476
-51
lines changed

biome.jsonc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@
2626
"all": true,
2727
"complexity": {
2828
"noBannedTypes": "warn"
29+
},
30+
"style": {
31+
"noNamespace": "off"
32+
},
33+
"performance": {
34+
"noBarrelFile": {
35+
"level": "off"
36+
}
2937
}
3038
}
3139
},

deno.jsonc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@suddenly-giovanni/std",
3-
"version": "0.0.3",
3+
"version": "0.0.4",
44
"exports": {
55
"./predicate": "./src/predicate/mod.ts",
66
"./option": "./src/option/mod.ts"

src/internal/equals.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/**
2+
* An interface containing operations for equality.
3+
*/
4+
export interface Equals {
5+
/**
6+
* Checks if `this` value is equal to`that` value.
7+
* @param that - The value to compare to.
8+
*/
9+
equals(that: unknown): boolean
10+
}

src/internal/inspectable.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ export interface Inspectable {
1111
*
1212
* Object.prototype.toString.call(new BoxedV1()); // '[object Object]'
1313
*
14+
* import { type Inspectable } from './inspectable.ts'
15+
*
1416
* class BoxedV2 implements Inspectable {
1517
* toString(): string {
1618
* throw new Error('Method not implemented.')
@@ -54,6 +56,8 @@ export interface Inspectable {
5456
* console.log(dogV1.toString()) // '[object Object]'
5557
* console.log(String(dogV1)) // '[object Object]'
5658
*
59+
* import { type Inspectable } from './inspectable.ts'
60+
*
5761
* class DogV2 implements Inspectable {
5862
* constructor(
5963
* readonly name: string,

src/option/option.test.ts

Lines changed: 144 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,43 @@
1-
import { describe, it } from '@std/testing/bdd'
2-
import { assertStrictEquals } from '@std/assert'
1+
import { assert, assertEquals, assertStrictEquals, assertThrows, equal } from '@std/assert'
2+
import { describe, it, test } from '@std/testing/bdd'
33
import { Option } from './option.ts'
44

55
describe('Option', () => {
66
describe('constructors', () => {
7+
it('should throw an error when trying to construct Option directly ', () => {
8+
assertThrows(() => {
9+
// @ts-expect-error - TSC does not allow instantiation of abstract classes, but what about runtime?
10+
new Option()
11+
})
12+
})
13+
714
it('Some', () => {
815
const some = Option.Some(1)
916
assertStrictEquals(some._tag, 'Some')
1017
assertStrictEquals(some.value, 1)
1118
})
1219

13-
it('None', () => {
14-
const none = Option.None()
15-
assertStrictEquals(none._tag, 'None')
20+
describe('None', () => {
21+
it('constructor', () => {
22+
const none = Option.None()
23+
assertStrictEquals(none._tag, 'None')
24+
})
25+
26+
it('None is a singleton ', () => {
27+
const none = Option.None()
28+
assert(Option.None() === none)
29+
assert(Object.is(Option.None(), Option.None()))
30+
})
31+
})
32+
33+
it('fromNullable', () => {
34+
assertStrictEquals(Option.fromNullable(2).equals(Option.Some(2)), true)
35+
assertStrictEquals(Option.fromNullable(0).equals(Option.Some(0)), true)
36+
assertStrictEquals(Option.fromNullable('').equals(Option.Some('')), true)
37+
assertStrictEquals(Option.fromNullable([]).equals(Option.Some([]), equal), true)
38+
39+
assertStrictEquals(Option.isNone(Option.fromNullable(null)), true)
40+
assertStrictEquals(Option.fromNullable(undefined).isNone(), true)
1641
})
1742
})
1843

@@ -23,21 +48,127 @@ describe('Option', () => {
2348
assertStrictEquals(Option.isOption({}), false)
2449
})
2550

26-
it('isNone', () => {
27-
assertStrictEquals(Option.isNone(Option.None()), true)
28-
assertStrictEquals(Option.isNone(Option.Some(1)), false)
51+
describe('isNone', () => {
52+
test('on Option static method ', () => {
53+
assertStrictEquals(Option.isNone(Option.None()), true)
54+
assertStrictEquals(Option.isNone(Option.Some(1)), false)
55+
})
56+
57+
test('on Option instances: Some and None ', () => {
58+
assertStrictEquals(Option.fromNullable(42).isNone(), false)
59+
assertStrictEquals(Option.fromNullable(undefined).isNone(), true)
60+
})
2961
})
3062

31-
it('isSome', () => {
32-
assertStrictEquals(Option.isSome(Option.None()), false)
33-
assertStrictEquals(Option.isSome(Option.Some(1)), true)
63+
describe('isSome', () => {
64+
test('on Option static method ', () => {
65+
assertStrictEquals(Option.isSome(Option.None()), false)
66+
assertStrictEquals(Option.isSome(Option.Some(1)), true)
67+
})
68+
69+
test('on Option instances: Some and None ', () => {
70+
assertStrictEquals(Option.fromNullable(null).isSome(), false)
71+
assertStrictEquals(Option.fromNullable(0).isSome(), true)
72+
})
3473
})
3574
})
3675

3776
describe('serialize', () => {
3877
it('toStringTag', () => {
39-
assertStrictEquals(String(Option.Some(1)), '[object Some]')
40-
assertStrictEquals(String(Option.None()), '[object None]')
78+
assertStrictEquals(
79+
String(Option.Some(1)),
80+
JSON.stringify(
81+
{
82+
_id: 'Option',
83+
_tag: 'Some',
84+
value: 1,
85+
},
86+
null,
87+
2,
88+
),
89+
)
90+
assertStrictEquals(
91+
String(Option.None()),
92+
JSON.stringify(
93+
{
94+
_id: 'Option',
95+
_tag: 'None',
96+
},
97+
null,
98+
2,
99+
),
100+
)
101+
})
102+
})
103+
104+
describe('Equal', () => {
105+
it('None ', () => {
106+
assertStrictEquals(Option.None().equals(Option.None()), true)
107+
assertStrictEquals(Option.None().equals(Option.Some('a')), false)
108+
})
109+
110+
it('Some', () => {
111+
assertStrictEquals(Option.Some('a').equals(Option.None()), false)
112+
assertStrictEquals(Option.Some(1).equals(Option.Some(2)), false)
113+
assertStrictEquals(Option.Some(1).equals(Option.Some(1)), true)
114+
115+
assertStrictEquals(Option.Some([]).equals(Option.Some([])), false)
116+
assertStrictEquals(Option.Some([]).equals(Option.Some([])), false)
117+
118+
const arr = [1, 2, 3]
119+
assertStrictEquals(Option.Some(arr).equals(Option.Some(arr)), true)
120+
assertStrictEquals(Option.Some(arr).equals(Option.Some([1, 2, 3])), false)
121+
})
122+
123+
it('should use the custom comparison predicate strategy', () => {
124+
assertStrictEquals(Option.Some([]).equals(Option.Some([]), equal), true)
125+
assertStrictEquals(Option.Some([1, 2, 3]).equals(Option.Some([1, 2, 3]), equal), true)
126+
assertStrictEquals(
127+
Option.Some({
128+
foo: {
129+
bar: {
130+
baz: 1,
131+
},
132+
},
133+
}).equals(
134+
Option.Some({
135+
foo: {
136+
bar: {
137+
baz: 1,
138+
},
139+
},
140+
}),
141+
equal,
142+
),
143+
true,
144+
)
145+
})
146+
})
147+
148+
describe('Inspectable', () => {
149+
const some = Option.Some(1)
150+
const none = Option.None()
151+
it('toStringTag', () => {
152+
assertStrictEquals(some[Symbol.toStringTag], 'Option.Some')
153+
assertStrictEquals(none[Symbol.toStringTag], 'Option.None')
154+
})
155+
156+
it('toJSON', () => {
157+
assertEquals(some.toJSON(), {
158+
_id: 'Option',
159+
_tag: 'Some',
160+
value: 1,
161+
})
162+
163+
assertEquals(none.toJSON(), {
164+
_id: 'Option',
165+
_tag: 'None',
166+
})
167+
})
168+
169+
it('toString', () => {
170+
assertStrictEquals(some.toString(), JSON.stringify(some.toJSON(), null, 2))
171+
assertStrictEquals(none.toString(), JSON.stringify(none.toJSON(), null, 2))
41172
})
42173
})
43174
})

0 commit comments

Comments
 (0)