Skip to content

Commit eeb7560

Browse files
author
Krzysztof Borowy
authored
[v2] Initial Core implementation (#136)
* Adds Core, factory implementations * TS types * Unit tests
1 parent 49bb8d9 commit eeb7560

File tree

13 files changed

+895
-49
lines changed

13 files changed

+895
-49
lines changed

.circleci/config.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,14 @@ jobs:
137137
name: Type check
138138
command: yarn test:types
139139

140+
"Test: unit":
141+
<<: *js_defaults
142+
steps:
143+
- *addWorkspace
144+
- run:
145+
name: Unit tests run
146+
command: yarn test:unit
147+
140148
"Test: iOS e2e":
141149
<<: *macos_defaults
142150
steps:
@@ -291,3 +299,6 @@ workflows:
291299
- "Test: types":
292300
requires:
293301
- "Setup environment"
302+
- "Test: unit":
303+
requires:
304+
- "Setup environment"

.prettierrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@
44
"trailingComma": "all",
55
"bracketSpacing": false,
66
"jsxBracketSameLine": true,
7-
"parser": "flow"
7+
"parser": "typescript"
88
}

babel.config.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
module.exports = {
2-
presets: ['@babel/preset-typescript'],
2+
presets: [
3+
'@babel/preset-typescript',
4+
[
5+
'@babel/preset-env',
6+
{
7+
targets: {
8+
node: 8,
9+
},
10+
},
11+
],
12+
],
313
plugins: [
414
'@babel/proposal-class-properties',
515
'@babel/proposal-object-rest-spread',
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
import AsyncStorage from '../src/AsyncStorage';
2+
3+
class StorageMock implements IStorageBackend<any> {
4+
getSingle = jest.fn();
5+
setSingle = jest.fn();
6+
getMany = jest.fn();
7+
setMany = jest.fn();
8+
removeSingle = jest.fn();
9+
removeMany = jest.fn();
10+
getKeys = jest.fn();
11+
dropStorage = jest.fn();
12+
}
13+
14+
describe('AsyncStorage', () => {
15+
const mockedStorage = new StorageMock();
16+
17+
beforeEach(() => {
18+
jest.resetAllMocks();
19+
});
20+
21+
describe('main API', () => {
22+
it.each(['get', 'set', 'remove'])(
23+
'handles single %s api call',
24+
async (methodName: string) => {
25+
const as = new AsyncStorage(mockedStorage, {
26+
logger: false,
27+
errorHandler: false,
28+
});
29+
30+
const key = 'myKey';
31+
const value = {
32+
name: 'Jerry',
33+
};
34+
35+
switch (methodName) {
36+
case 'get': {
37+
await as.get(key);
38+
expect(mockedStorage.getSingle).toBeCalledWith(key, null);
39+
break;
40+
}
41+
42+
case 'set': {
43+
await as.set(key, value);
44+
expect(mockedStorage.setSingle).toBeCalledWith(key, value, null);
45+
break;
46+
}
47+
48+
case 'remove': {
49+
await as.remove(key);
50+
expect(mockedStorage.removeSingle).toBeCalledWith(key, null);
51+
break;
52+
}
53+
}
54+
},
55+
);
56+
57+
it.each(['set', 'read', 'remove'])(
58+
'handles basic multi %s api call',
59+
async (methodName: string) => {
60+
const keys = ['key1', 'key2', 'key3'];
61+
const keyValues = [
62+
{key1: 'value1'},
63+
{key2: 'value2'},
64+
{key3: 'value3'},
65+
];
66+
67+
const as = new AsyncStorage(mockedStorage, {
68+
logger: false,
69+
errorHandler: false,
70+
});
71+
72+
switch (methodName) {
73+
case 'get': {
74+
await as.getMultiple(keys);
75+
expect(mockedStorage.getMany).toBeCalledWith(keys, null);
76+
break;
77+
}
78+
79+
case 'set': {
80+
await as.setMultiple(keyValues);
81+
expect(mockedStorage.setMany).toBeCalledWith(keyValues, null);
82+
break;
83+
}
84+
85+
case 'remove': {
86+
await as.removeMultiple(keys);
87+
expect(mockedStorage.removeMany).toBeCalledWith(keys, null);
88+
break;
89+
}
90+
}
91+
},
92+
);
93+
94+
it.each(['instance', 'getKeys', 'clearStorage'])(
95+
'handles %s api call',
96+
async (methodName: string) => {
97+
const asyncStorage = new AsyncStorage(mockedStorage, {
98+
logger: false,
99+
errorHandler: false,
100+
});
101+
102+
switch (methodName) {
103+
case 'instance': {
104+
expect(asyncStorage.instance()).toBe(mockedStorage);
105+
break;
106+
}
107+
108+
case 'getKeys': {
109+
mockedStorage.getKeys.mockImplementationOnce(() => [
110+
'key1',
111+
'key2',
112+
]);
113+
const keys = await asyncStorage.getKeys();
114+
expect(keys).toEqual(['key1', 'key2']);
115+
break;
116+
}
117+
118+
case 'dropStorage': {
119+
await asyncStorage.clearStorage();
120+
expect(mockedStorage.dropStorage).toBeCalledTimes(1);
121+
break;
122+
}
123+
}
124+
},
125+
);
126+
});
127+
describe('utils', () => {
128+
it('uses logger when provided', async () => {
129+
const loggerFunc = jest.fn();
130+
131+
const as = new AsyncStorage(mockedStorage, {
132+
logger: loggerFunc,
133+
errorHandler: false,
134+
});
135+
136+
await as.get('key');
137+
expect(loggerFunc).toBeCalledTimes(1);
138+
expect(loggerFunc).toBeCalledWith({action: 'read-single', key: 'key'});
139+
});
140+
141+
it('uses error handler when provided', async () => {
142+
const errorHandler = jest.fn();
143+
144+
const error = new Error('Fatal!');
145+
mockedStorage.getSingle.mockImplementationOnce(async () => {
146+
throw error;
147+
});
148+
149+
const as = new AsyncStorage(mockedStorage, {
150+
errorHandler,
151+
logger: false,
152+
});
153+
154+
await as.get('key');
155+
156+
expect(errorHandler).toBeCalledTimes(1);
157+
expect(errorHandler).toBeCalledWith(error);
158+
});
159+
});
160+
});

core/__tests__/core.test.ts

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import Factory from '../src/';
2+
import {simpleLogger, simpleErrorHandler} from '../src/defaults';
3+
4+
describe('AsyncStorageFactory', () => {
5+
it('Throws when tried to instantiate', () => {
6+
expect(() => new Factory()).toThrow(
7+
"[AsyncStorage] AsyncStorageFactory must not be instantiated.\nInstead, use static functions, like 'create' to get AsyncStorage instance.",
8+
);
9+
});
10+
});
11+
12+
describe('SimpleLogger', () => {
13+
beforeAll(() => {
14+
jest.spyOn(console, 'log').mockImplementation();
15+
});
16+
17+
beforeEach(() => {
18+
console.log.mockReset();
19+
});
20+
21+
afterAll(() => {
22+
console.log.mockRestore();
23+
});
24+
25+
it('logs basic info about action', () => {
26+
const actionInfo: LoggerAction = {
27+
action: 'save-single',
28+
key: 'MyKey',
29+
value: 'MyValue',
30+
};
31+
32+
simpleLogger(actionInfo);
33+
34+
expect(console.log).toBeCalledTimes(1);
35+
36+
const callArgs = console.log.mock.calls[0][0];
37+
expect(callArgs).toContain('[AsyncStorage]');
38+
expect(callArgs).toContain(actionInfo.key);
39+
expect(callArgs).toContain(actionInfo.value);
40+
});
41+
42+
it('handles unknown action by logging it', () => {
43+
const actionInfo: LoggerAction = {
44+
// @ts-ignore need to handle unknown
45+
action: 'my-action',
46+
};
47+
48+
simpleLogger(actionInfo);
49+
50+
expect(console.log).toBeCalledTimes(1);
51+
expect(console.log).toBeCalledWith(
52+
'[AsyncStorage] Unknown action: my-action',
53+
);
54+
});
55+
});
56+
57+
describe('SimpleErrorHandler', () => {
58+
beforeAll(() => {
59+
jest.spyOn(console, 'error').mockImplementation();
60+
});
61+
62+
beforeEach(() => {
63+
console.error.mockReset();
64+
});
65+
66+
afterAll(() => {
67+
console.error.mockRestore();
68+
});
69+
it('logs error when it is a string', () => {
70+
const errorMessage = 'Fatal!';
71+
72+
simpleErrorHandler(errorMessage);
73+
74+
expect(console.error).toBeCalledTimes(1);
75+
expect(console.error).toBeCalledWith(errorMessage);
76+
});
77+
78+
it('logs error when it is an Error', () => {
79+
const error = new Error('Fatal!');
80+
81+
simpleErrorHandler(error);
82+
83+
expect(console.error).toBeCalledTimes(1);
84+
expect(console.error).toBeCalledWith('Fatal!');
85+
});
86+
});

0 commit comments

Comments
 (0)