Skip to content

Commit 8e6b855

Browse files
Issue #47 Port over Typescript implementation of Do.Try pattern
add typescript implementation of Do.try pattern
2 parents 54446c6 + 2dd503c commit 8e6b855

8 files changed

Lines changed: 466 additions & 0 deletions

File tree

src/tests/factories/factory-type.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
const FactoryType = {
2+
ResultErrorRecord: "ResultErrorRecord",
3+
ResultRecord: "ResultRecord",
24
StubResourceRecord: "StubResourceRecord",
35
};
46

src/tests/factories/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
export { AxiosResponseFactory } from "andculturecode-javascript-testing";
2+
export * from "./result-error-record-factory";
3+
export * from "./result-record-factory";
24
export * from "./stub-resource-record-factory";
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { ResultErrorRecord } from "../../view-models/result-error-record";
2+
import { Factory } from "rosie";
3+
import { FactoryType } from "./factory-type";
4+
5+
// -------------------------------------------------------------------------------------------------
6+
// #region Factory
7+
// -------------------------------------------------------------------------------------------------
8+
9+
const ResultErrorRecordFactory = Factory.define<ResultErrorRecord>(
10+
FactoryType.ResultErrorRecord,
11+
ResultErrorRecord
12+
)
13+
.sequence("key", (i: number) => `TEST_ERROR_KEY_${i}`)
14+
.sequence("message", (i: number) => `Test error message ${i}`);
15+
16+
// #endregion Factory
17+
18+
// -------------------------------------------------------------------------------------------------
19+
// #region Exports
20+
// -------------------------------------------------------------------------------------------------
21+
22+
export { ResultErrorRecordFactory };
23+
24+
// #endregion Exports
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { ResultRecord } from "../../view-models/result-record";
2+
import { Factory } from "rosie";
3+
import { FactoryType } from "./factory-type";
4+
5+
// -------------------------------------------------------------------------------------------------
6+
// #region Factory
7+
// -------------------------------------------------------------------------------------------------
8+
9+
const ResultRecordFactory = Factory.define<ResultRecord<any>>(
10+
FactoryType.ResultRecord,
11+
ResultRecord
12+
);
13+
14+
// #endregion Factory
15+
16+
// -------------------------------------------------------------------------------------------------
17+
// #region Exports
18+
// -------------------------------------------------------------------------------------------------
19+
20+
export { ResultRecordFactory };
21+
22+
// #endregion Exports

src/types/do-try-types.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { ResultRecord } from "../view-models/result-record";
2+
3+
// -----------------------------------------------------------------------------------------
4+
// #region Types
5+
// -----------------------------------------------------------------------------------------
6+
7+
type AsyncWorkload<T> = () => Promise<T>;
8+
type SyncWorkload<T> = () => T;
9+
type CatchHandler<T> = (result?: ResultRecord<T>, error?: any) => void;
10+
type FinallyHandler = () => void;
11+
12+
// #endregion Types
13+
14+
// -----------------------------------------------------------------------------------------
15+
// #region Exports
16+
// -----------------------------------------------------------------------------------------
17+
18+
export { AsyncWorkload, SyncWorkload, CatchHandler, FinallyHandler };
19+
20+
// #endregion Exports

src/utilities/do-try.test.ts

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
import { ResultRecord } from "../view-models/result-record";
2+
import {
3+
ResultErrorRecordFactory,
4+
ResultRecordFactory,
5+
StubResourceRecordFactory,
6+
} from "../tests/factories";
7+
import { Do, DoSync } from "../utilities/do-try";
8+
import { PolyfillUtils } from "../utilities/polyfill-utils";
9+
import { StubResourceRecord } from "../tests/stubs/stub-resource-record";
10+
11+
PolyfillUtils.registerPromiseFinallyPolyfill();
12+
13+
describe("do-try.ts", () => {
14+
describe("Do.try", () => {
15+
it("When validation errors occur (i.e. error is a ResultRecord), then passes typed errors to catch handler", async () => {
16+
// Arrange
17+
const workload = async () => {
18+
throw ResultRecordFactory.build({
19+
errors: ResultErrorRecordFactory.buildList(1),
20+
});
21+
};
22+
23+
// Act & Assert
24+
// since jest is using node promises, it fails the test as soon as the error
25+
// is thrown if we actually await the promise. expect.assertions(5) waits for
26+
// 5 assertions to be called.
27+
Do.try(workload).catch(
28+
(result?: ResultRecord<any>, error?: any) => {
29+
expect(result).not.toBeNil();
30+
expect(error).toBeUndefined();
31+
expect(result!.resultObject).toBeUndefined();
32+
expect(result!.errors).toHaveLength(1);
33+
}
34+
);
35+
expect.assertions(4);
36+
});
37+
38+
it("When passed an async method and a Javascript error occurs, then passes along regular error", async () => {
39+
// Arrange
40+
const workload = async () => {
41+
throw Error();
42+
};
43+
44+
// Act & Assert
45+
Do.try(workload).catch(
46+
(result?: ResultRecord<any>, error?: any) => {
47+
expect(error).not.toBeNil();
48+
expect(result).toBeUndefined();
49+
}
50+
);
51+
expect.assertions(2);
52+
});
53+
54+
it("When no errors occur, then catch handler is never called", async () => {
55+
// Arrange
56+
const catchHandler = jest.fn();
57+
const workload = jest.fn();
58+
59+
// Act
60+
await Do.try(workload)
61+
.catch(catchHandler)
62+
.getAwaiter();
63+
64+
// Assert
65+
expect(catchHandler).not.toHaveBeenCalled();
66+
});
67+
68+
it("When no errors occur, then finally handler is still called", async () => {
69+
// Arrange
70+
const finallyHandler = jest.fn();
71+
const workload = jest.fn();
72+
73+
// Act
74+
await Do.try(workload)
75+
.finally(finallyHandler)
76+
.getAwaiter();
77+
78+
// Assert
79+
expect(finallyHandler).toHaveBeenCalled();
80+
});
81+
82+
it("When errors occur, then finally handler is still called", async () => {
83+
// Arrange
84+
const catchHandler = jest.fn();
85+
const workload = async () => {
86+
throw Error();
87+
};
88+
let finallyCalled = false;
89+
90+
// Act & Assert
91+
Do.try(workload)
92+
.catch(catchHandler)
93+
.finally(() => {
94+
finallyCalled = true;
95+
expect(finallyCalled).toBeTrue();
96+
});
97+
});
98+
});
99+
100+
describe("DoSync.try", () => {
101+
it("When no errors occur, then .execute() returns the workload return value", () => {
102+
// Arrange
103+
const workload = () => StubResourceRecordFactory.build();
104+
105+
// Act
106+
const result = DoSync.try(workload).execute();
107+
108+
// Assert
109+
expect(result).not.toBeNil();
110+
expect(result).toBeInstanceOf(StubResourceRecord);
111+
});
112+
113+
it("When Javascript errors occur, then catch handler is passed regular error and return value is undefined", () => {
114+
// Arrange
115+
const workload = () => {
116+
throw Error();
117+
};
118+
119+
// Act
120+
const result = DoSync.try(workload)
121+
.catch((result?: ResultRecord<any>, error?: any) => {
122+
expect(result).toBeUndefined();
123+
expect(error).not.toBeNil();
124+
})
125+
.execute();
126+
127+
// Assert
128+
expect(result).toBeUndefined();
129+
});
130+
131+
it("When validation error occurs, then catch handler is passed ResultRecord and return value is undefined", () => {
132+
// Arrange
133+
const workload = () => {
134+
throw ResultRecordFactory.build({
135+
errors: ResultErrorRecordFactory.buildList(1),
136+
});
137+
};
138+
139+
// Act
140+
const result = DoSync.try(workload)
141+
.catch((result?: ResultRecord<any>, error?: any) => {
142+
expect(error).toBeUndefined();
143+
expect(result).not.toBeNil();
144+
expect(result!.resultObject).toBeUndefined();
145+
expect(result!.errors).toHaveLength(1);
146+
})
147+
.execute();
148+
149+
// Assert
150+
expect(result).toBeUndefined();
151+
});
152+
153+
it("When errors occur, then finally handler is still called and return value is undefined", () => {
154+
// Arrange
155+
const finallyHandler = jest.fn();
156+
const workload = () => {
157+
throw Error();
158+
};
159+
160+
// Act
161+
const result = DoSync.try(workload)
162+
.finally(finallyHandler)
163+
.execute();
164+
165+
// Assert
166+
expect(result).toBeUndefined();
167+
expect(finallyHandler).toHaveBeenCalled();
168+
});
169+
170+
it("When no errors occur, then finally handler is still called and return value is not undefined", () => {
171+
// Arrange
172+
const finallyHandler = jest.fn();
173+
const workload = () => StubResourceRecordFactory.build();
174+
175+
// Act
176+
const result = DoSync.try(workload)
177+
.finally(finallyHandler)
178+
.execute();
179+
180+
// Assert
181+
expect(result).toBeInstanceOf(StubResourceRecord);
182+
expect(finallyHandler).toHaveBeenCalled();
183+
});
184+
});
185+
});

0 commit comments

Comments
 (0)