Skip to content

Commit 2336c47

Browse files
committed
Implement Postcode.parse
- Returns eith a ValidPostcode or InvalidPostcode instance with same properties - InvalidPostcode has a fixed definition - ValidPostcode provides accessor methods to allow for easy destructuring and cleaner access
1 parent 4d6c841 commit 2336c47

2 files changed

Lines changed: 260 additions & 0 deletions

File tree

lib/index.ts

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,74 @@ interface Parser {
66
(postcode: string): string | null;
77
}
88

9+
class ValidPostcode {
10+
private instance: Postcode;
11+
12+
constructor(postcode: string) {
13+
this.instance = new Postcode(postcode);
14+
}
15+
16+
get valid(): boolean {
17+
return this.instance.valid();
18+
}
19+
20+
get postcode(): string {
21+
return <string>this.instance.normalise();
22+
}
23+
24+
get incode(): string {
25+
return <string>this.instance.incode();
26+
}
27+
28+
get outcode(): string {
29+
return <string>this.instance.outcode();
30+
}
31+
32+
get area(): string {
33+
return <string>this.instance.area();
34+
}
35+
36+
get district(): string {
37+
return <string>this.instance.district();
38+
}
39+
40+
get subDistrict(): string {
41+
return <string>this.instance.subDistrict();
42+
}
43+
44+
get sector(): string {
45+
return <string>this.instance.sector();
46+
}
47+
48+
get unit(): string {
49+
return <string>this.instance.unit();
50+
}
51+
}
52+
53+
type InvalidPostcode = {
54+
valid: false;
55+
postcode: null;
56+
incode: null;
57+
outcode: null;
58+
area: null;
59+
district: null;
60+
subDistrict: null;
61+
sector: null;
62+
unit: null;
63+
};
64+
65+
const invalidPostcode: InvalidPostcode = Object.freeze({
66+
valid: false,
67+
postcode: null,
68+
incode: null,
69+
outcode: null,
70+
area: null,
71+
district: null,
72+
subDistrict: null,
73+
sector: null,
74+
unit: null
75+
});
76+
977
/**
1078
* Return first elem of input is RegExpMatchArray or null if input null
1179
*/
@@ -219,6 +287,11 @@ class Postcode {
219287
return outcode.match(validOutcodeRegex) !== null;
220288
}
221289

290+
static parse(postcode: string): ValidPostcode | InvalidPostcode {
291+
if (isValid(postcode)) return new ValidPostcode(postcode);
292+
return { ...invalidPostcode };
293+
}
294+
222295
valid(): boolean {
223296
return this._valid;
224297
}

test/parse.unit.ts

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
import { assert } from "chai";
2+
import { loadFixtures } from "./util/helper";
3+
import Postcode from "../lib/index";
4+
5+
const INVALID_POSTCODE = Object.freeze({
6+
valid: false,
7+
postcode: null,
8+
incode: null,
9+
outcode: null,
10+
area: null,
11+
district: null,
12+
subDistrict: null,
13+
sector: null,
14+
unit: null
15+
});
16+
17+
describe("Postcode.parse", () => {
18+
describe("invalid postcode", () => {
19+
it("returns an invalid postcode object", () => {
20+
const {
21+
valid,
22+
postcode,
23+
incode,
24+
outcode,
25+
area,
26+
district,
27+
subDistrict,
28+
sector,
29+
unit
30+
} = Postcode.parse("foo");
31+
assert.deepEqual(
32+
{
33+
valid,
34+
postcode,
35+
incode,
36+
outcode,
37+
area,
38+
district,
39+
subDistrict,
40+
sector,
41+
unit
42+
},
43+
{ ...INVALID_POSTCODE }
44+
);
45+
});
46+
});
47+
48+
describe("with valid postcode", () => {
49+
describe("Postcode#Valid", async () => {
50+
it("should return true for postcodes that look correct", async () => {
51+
const { tests } = await loadFixtures("validation.json");
52+
tests.forEach(({ base, expected }) => {
53+
const result = Postcode.parse(base);
54+
assert.equal(result.valid, Boolean(expected));
55+
});
56+
});
57+
58+
describe("Postcode normalisation", () => {
59+
it("should correctly normalise postcodes", async () => {
60+
const { tests } = await loadFixtures("normalisation.json");
61+
tests.forEach(({ base, expected }) => {
62+
const result = Postcode.parse(base);
63+
assert.equal(result.postcode, expected);
64+
});
65+
});
66+
67+
it("should return null if invalid postcode", () => {
68+
assert.isNull(Postcode.parse("Definitly bogus").postcode);
69+
});
70+
});
71+
72+
describe("Postcode.validOutcode", () => {
73+
it("should return true for valid outcodes", async () => {
74+
const { tests } = await loadFixtures("outcodes.json");
75+
tests.forEach(({ expected }) =>
76+
assert.isTrue(Postcode.validOutcode(expected))
77+
);
78+
});
79+
80+
it("should return false for invalid outcode", () => {
81+
const invalidOutcodes = ["BOGUS", "Hello there", "12345"];
82+
invalidOutcodes.forEach(code =>
83+
assert.isFalse(Postcode.validOutcode(code))
84+
);
85+
});
86+
});
87+
88+
describe("Incode parsing", () => {
89+
it("should correctly parse incodes", async () => {
90+
const { tests } = await loadFixtures("incodes.json");
91+
tests.forEach(({ base, expected }) => {
92+
const result = Postcode.parse(base);
93+
assert.equal(result.incode, expected);
94+
});
95+
});
96+
97+
it("should return null if invalid postcode", () => {
98+
assert.isNull(Postcode.parse("Definitly bogus").incode);
99+
});
100+
});
101+
102+
describe("Outcode parsing", () => {
103+
it("should correctly parse outcodes", async () => {
104+
const { tests } = await loadFixtures("outcodes.json");
105+
tests.forEach(({ base, expected }) => {
106+
const result = Postcode.parse(base);
107+
assert.equal(result.outcode, expected);
108+
});
109+
});
110+
111+
it("should return null if invalid postcode", () => {
112+
assert.isNull(Postcode.parse("Definitly bogus").outcode);
113+
});
114+
});
115+
116+
describe("Area parsing", () => {
117+
it("should correctly parse areas", async () => {
118+
const { tests } = await loadFixtures("areas.json");
119+
tests.forEach(({ base, expected }) => {
120+
const result = Postcode.parse(base);
121+
assert.equal(result.area, expected);
122+
});
123+
});
124+
125+
it("should return null if invalid postcode", () => {
126+
assert.isNull(Postcode.parse("Definitely bogus").area);
127+
});
128+
});
129+
130+
describe("District parsing", () => {
131+
it("should correctly parse districts", async () => {
132+
const { tests } = await loadFixtures("districts.json");
133+
tests.forEach(({ base, expected }) => {
134+
const result = Postcode.parse(base);
135+
assert.equal(result.district, expected);
136+
});
137+
});
138+
139+
it("should return null if invalid postcode", () => {
140+
assert.isNull(Postcode.parse("Definitely bogus").district);
141+
});
142+
});
143+
144+
describe("Sub-district parsing", () => {
145+
it("should correctly parse sub-districts", async () => {
146+
const { tests } = await loadFixtures("sub-districts.json");
147+
tests.forEach(({ base, expected }) => {
148+
const result = Postcode.parse(base);
149+
assert.equal(result.subDistrict, expected);
150+
});
151+
});
152+
153+
it("should return null if invalid postcode", () => {
154+
assert.isNull(Postcode.parse("Definitely bogus").subDistrict);
155+
});
156+
});
157+
158+
describe("Sector parsing", () => {
159+
it("should correctly parse sectors", async () => {
160+
const { tests } = await loadFixtures("sectors.json");
161+
tests.forEach(({ base, expected }) => {
162+
const result = Postcode.parse(base);
163+
assert.equal(result.sector, expected);
164+
});
165+
});
166+
167+
it("should return null if invalid postcode", () => {
168+
assert.isNull(Postcode.parse("Definitely bogus").sector);
169+
});
170+
});
171+
172+
describe("Unit parsing", () => {
173+
it("should correctly parse units", async () => {
174+
const { tests } = await loadFixtures("units.json");
175+
tests.forEach(({ base, expected }) => {
176+
const result = Postcode.parse(base);
177+
assert.equal(result.unit, expected);
178+
});
179+
});
180+
181+
it("should return null if invalid postcode", () => {
182+
assert.isNull(Postcode.parse("Definitely bogus").unit);
183+
});
184+
});
185+
});
186+
});
187+
});

0 commit comments

Comments
 (0)