Strongly type Form lines.
[ustaxlib.git] / src / Line.test.ts
1 import { Line, AccumulatorLine, InputLine, ReferenceLine, ComputedLine } from './Line';
2 import Form from './Form';
3 import TaxReturn from './TaxReturn';
4 import { NotFoundError } from './Errors';
5
6 class ConstantLine<T> extends Line<T> {
7 private _k: T;
8
9 constructor(id: string, k: T) {
10 super(id, `Constant ${k}`);
11 this._k = k;
12 }
13
14 value(tr: TaxReturn): T {
15 return this._k;
16 }
17 };
18
19 test('computed line', () => {
20 const tr = new TaxReturn(2019);
21 const l = new ComputedLine<number>('A',
22 (taxReturn: TaxReturn, line: ComputedLine<number>): number => {
23 expect(taxReturn).toBe(tr);
24 expect(line).toBe(l);
25 return 42;
26 },
27 'Computed Line A');
28 expect(l.value(tr)).toBe(42);
29 expect(l.id).toBe('A');
30 expect(l.description).toBe('Computed Line A');
31 });
32
33 test('reference line', () => {
34 class TestForm extends Form<TestForm['_lines']> {
35 readonly name = 'Form 1';
36 protected readonly _lines = {
37 '6b': new ConstantLine('6b', 12.34)
38 };
39 };
40
41 const tr = new TaxReturn(2019);
42 tr.addForm(new TestForm());
43
44 const l1 = new ReferenceLine<number>('C', 'Form 1', '6b');
45 expect(l1.value(tr)).toBe(12.34);
46
47 const l2 = new ReferenceLine<number>('x', 'Form 2', '6b');
48 expect(() => l2.value(tr)).toThrow(NotFoundError);
49
50 const l3 = new ReferenceLine<number>('y', 'Form 1', '7a');
51 expect(() => l3.value(tr)).toThrow(NotFoundError);
52 });
53
54 test('input line', () => {
55 interface Input {
56 key: string;
57 key2?: string;
58 }
59 class TestForm extends Form<TestForm['_lines'], Input> {
60 readonly name = 'F1';
61 protected readonly _lines = {
62 '1': new InputLine<Input>('1', 'key'),
63 '2': new InputLine<Input>('2', 'key2')
64 };
65 };
66 const tr = new TaxReturn(2019);
67 const f = new TestForm({ 'key': 'value' });
68
69 expect(f.getLine('1').value(tr)).toBe('value');
70
71 const l2 = f.getLine('2');
72 expect(() => l2.value(tr)).toThrow(NotFoundError);
73 });
74
75 test('line stack', () => {
76 class FormZ extends Form<FormZ['_lines'], {'input': number}> {
77 readonly name = 'Z';
78 protected readonly _lines = {
79 '3': new InputLine<any, any>('3', 'input')
80 }
81 };
82
83 class FormZ2 extends Form<FormZ2['_lines']> {
84 readonly name = 'Z-2';
85 protected readonly _lines = {
86 '2c': new ComputedLine<number>('2c', (tr: TaxReturn, l: Line<number>): any => {
87 return tr.getForm('Z').getLine('3').value(tr) * 0.2;
88 })
89 };
90 };
91
92 const tr = new TaxReturn(2019);
93 tr.addForm(new FormZ({ 'input': 100 }));
94 tr.addForm(new FormZ2());
95
96 const l = new ReferenceLine<number>('32', 'Z-2', '2c');
97 expect(l.value(tr)).toBe(20);
98 });
99
100 test('accumulator line', () => {
101 class TestForm extends Form<TestForm['_lines']> {
102 readonly name = 'Form B';
103 readonly supportsMultipleCopies = true;
104 protected readonly _lines = {
105 g: new ConstantLine<number>('g', 100.25)
106 };
107 };
108
109 const tr = new TaxReturn(2019);
110 tr.addForm(new TestForm());
111 tr.addForm(new TestForm());
112 tr.addForm(new TestForm());
113
114 const l = new AccumulatorLine('line', 'Form B', 'g');
115 expect(l.value(tr)).toBe(300.75);
116 });