@computools/react-native-template-controller 0.0.4 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@computools/react-native-template-controller",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
4
4
  "description": "Computools react native template by using mobx as controllers",
5
5
  "main": "template.config.js",
6
6
  "scripts": {
@@ -7,7 +7,8 @@
7
7
  "ios": "react-native run-ios",
8
8
  "lint": "eslint .",
9
9
  "start": "react-native start",
10
- "test": "jest"
10
+ "test": "jest",
11
+ "test-coverage": "jest test --coverage --watchAll=false"
11
12
  },
12
13
  "dependencies": {
13
14
  "@hookform/resolvers": "2.9.11",
@@ -48,6 +49,32 @@
48
49
  "typescript": "4.8.4"
49
50
  },
50
51
  "jest": {
51
- "preset": "react-native"
52
+ "preset": "react-native",
53
+ "collectCoverageFrom": [
54
+ "**/*.ts",
55
+ "**/*.controller.ts",
56
+ "**/*.impl.ts",
57
+ "**/*.repo.ts",
58
+ "**/*.service.ts",
59
+ "!node_modules/*",
60
+ "!**/types/*",
61
+ "!**/*.routes.ts"
62
+ ],
63
+ "moduleFileExtensions": [
64
+ "ts",
65
+ "tsx",
66
+ "js",
67
+ "jsx",
68
+ "json",
69
+ "node"
70
+ ],
71
+ "coverageThreshold": {
72
+ "global": {
73
+ "branches": 100,
74
+ "functions": 100,
75
+ "lines": 100,
76
+ "statements": 100
77
+ }
78
+ }
52
79
  }
53
- }
80
+ }
@@ -0,0 +1,148 @@
1
+ import {Http} from '../http';
2
+ import JSONResponse from './mocks/picsum_response.json';
3
+ import {token} from './mocks/token.json';
4
+
5
+ interface HttpConstructor {
6
+ baseURL?: string;
7
+ headers?: {[headerName: string]: string | number};
8
+ }
9
+
10
+ interface AxiosResponse {
11
+ url: string;
12
+ someData: Object;
13
+ config: HttpConstructor;
14
+ }
15
+
16
+ const mockedResponse = (config: HttpConstructor) => (url: string) => {
17
+ if (url.includes('error')) {
18
+ throw new Error(url);
19
+ }
20
+
21
+ return {data: {config, url, someData: JSONResponse}};
22
+ };
23
+
24
+ jest.mock('axios', () => ({
25
+ create: jest.fn((config: HttpConstructor) => ({
26
+ get: mockedResponse(config),
27
+ post: mockedResponse(config),
28
+ put: mockedResponse(config),
29
+ delete: mockedResponse(config),
30
+ })),
31
+ }));
32
+
33
+ describe('Http', () => {
34
+ it('empty constructor', async () => {
35
+ const http = new Http();
36
+
37
+ await checkSuccessHttpRequests({baseURL: '', headers: {}}, http);
38
+ await checkFaildHttpRequests(http);
39
+ });
40
+
41
+ it('base URL constructor', async () => {
42
+ const baseURL = 'https://picsum.photos/v2/list';
43
+ const http = new Http(baseURL);
44
+
45
+ await checkSuccessHttpRequests({baseURL, headers: {}}, http);
46
+ await checkFaildHttpRequests(http);
47
+ });
48
+
49
+ it('base URL & default headers constructor', async () => {
50
+ const baseURL = 'https://picsum.photos/v2/list';
51
+ const headers = {'Content-Type': 'multipart/form-data'};
52
+ const http = new Http(baseURL, headers);
53
+
54
+ await checkSuccessHttpRequests({baseURL, headers}, http);
55
+ await checkFaildHttpRequests(http);
56
+ });
57
+
58
+ it('setBaseURL method', async () => {
59
+ const http = new Http();
60
+
61
+ const baseURL = 'http://www.redmine.com';
62
+ http.setBaseURL(baseURL);
63
+
64
+ await checkSuccessHttpRequests({baseURL, headers: {}}, http);
65
+ await checkFaildHttpRequests(http);
66
+ });
67
+
68
+ it('addBearerToken method', async () => {
69
+ const baseURL = 'http://www.redmine.com';
70
+ const http = new Http(baseURL);
71
+ http.addBearerToken(token);
72
+
73
+ await checkSuccessHttpRequests({baseURL, headers: {Authorization: `Bearer ${token}`}}, http);
74
+ await checkFaildHttpRequests(http);
75
+ });
76
+
77
+ it('addHeader method', async () => {
78
+ const http = new Http();
79
+
80
+ const headerName = 'AppVersion';
81
+ const headerValue = 'AppVersion';
82
+ http.addHeader(headerName, headerValue);
83
+
84
+ await checkSuccessHttpRequests({baseURL: '', headers: {[headerName]: headerValue}}, http);
85
+ await checkFaildHttpRequests(http);
86
+ });
87
+
88
+ it('cleanHeaders method', async () => {
89
+ const baseURL = 'http://redmine.com';
90
+ const headers = {'Content-Type': 'multipart/form-data'};
91
+ const http = new Http(baseURL, headers);
92
+
93
+ await checkSuccessHttpRequests({baseURL, headers}, http);
94
+
95
+ http.cleanHeaders();
96
+
97
+ await checkSuccessHttpRequests({baseURL, headers: {}}, http);
98
+ await checkFaildHttpRequests(http);
99
+ });
100
+
101
+ const checkSuccessHttpRequests = async (expectConfig: HttpConstructor, http: Http) => {
102
+ const reqGetUrl = 'https://picsum.photos/v2/list';
103
+ const getRes = await http.get<AxiosResponse>(reqGetUrl);
104
+
105
+ expect(getRes.config).toStrictEqual(expectConfig);
106
+ expect(getRes.url).toStrictEqual(reqGetUrl);
107
+ expect(getRes.someData).toStrictEqual(JSONResponse);
108
+
109
+ const reqPostUrl = 'https://picsum.photos/v2/list/1';
110
+ const postRes = await http.post<AxiosResponse>(reqPostUrl);
111
+
112
+ expect(postRes.config).toStrictEqual(expectConfig);
113
+ expect(postRes.url).toStrictEqual(reqPostUrl);
114
+ expect(postRes.someData).toStrictEqual(JSONResponse);
115
+
116
+ const reqPutUrl = 'https://picsum.photos/v2/list/1';
117
+ const putRes = await http.put<AxiosResponse>(reqPutUrl);
118
+
119
+ expect(putRes.config).toStrictEqual(expectConfig);
120
+ expect(putRes.url).toStrictEqual(reqPutUrl);
121
+ expect(putRes.someData).toStrictEqual(JSONResponse);
122
+
123
+ const reqDeleteUrl = 'https://picsum.photos/v2/list/1';
124
+ const deleteRes = await http.delete<AxiosResponse>(reqDeleteUrl);
125
+
126
+ expect(deleteRes.config).toStrictEqual(expectConfig);
127
+ expect(deleteRes.url).toStrictEqual(reqDeleteUrl);
128
+ expect(deleteRes.someData).toStrictEqual(JSONResponse);
129
+ };
130
+
131
+ const checkFaildHttpRequests = async (http: Http) => {
132
+ const reqGetUrl = 'https://picsum.photos/v2/list_error';
133
+ const getRes = http.get<AxiosResponse>(reqGetUrl);
134
+ await expect(getRes).rejects.toThrow(reqGetUrl);
135
+
136
+ const reqPostUrl = 'https://picsum.photos/v2/list/1_error';
137
+ const postRes = http.post<AxiosResponse>(reqPostUrl);
138
+ await expect(postRes).rejects.toThrow(reqPostUrl);
139
+
140
+ const reqPutUrl = 'https://picsum.photos/v2/list_error/1';
141
+ const putRes = http.put<AxiosResponse>(reqPutUrl);
142
+ await expect(putRes).rejects.toThrow(reqPutUrl);
143
+
144
+ const reqDeleteUrl = 'https://picsum.photos/v2/list_error/1';
145
+ const deleteRes = http.delete<AxiosResponse>(reqDeleteUrl);
146
+ await expect(deleteRes).rejects.toThrow(reqDeleteUrl);
147
+ };
148
+ });
@@ -0,0 +1,17 @@
1
+ import {injector} from '../injector';
2
+
3
+ describe('Injector', () => {
4
+ it('check with abstract class', async () => {
5
+ abstract class SomeAbstractClass {
6
+ public name: string = 'John Doy';
7
+ }
8
+ class SomeClass implements SomeAbstractClass {
9
+ public name: string = 'Test Jhon';
10
+ }
11
+
12
+ const value = new SomeClass();
13
+
14
+ injector.set(SomeAbstractClass, value);
15
+ expect(injector.get(SomeAbstractClass).name).toStrictEqual(value.name);
16
+ });
17
+ });
@@ -0,0 +1,37 @@
1
+ import {MMKVMemory} from '../memory';
2
+ import JSONUser from './mocks/user.json';
3
+
4
+ describe('Memory', () => {
5
+ let memory: MMKVMemory;
6
+
7
+ beforeEach(() => {
8
+ memory = new MMKVMemory();
9
+ });
10
+
11
+ it('success save/get', () => {
12
+ const key = 'TEST_USER';
13
+ memory.save(key, JSONUser);
14
+
15
+ const user = memory.get(key);
16
+ expect(user).toStrictEqual(JSONUser);
17
+ });
18
+
19
+ it('success remove/get', () => {
20
+ const key = 'TEST_USER';
21
+ memory.save(key, JSONUser);
22
+
23
+ const user = memory.get(key);
24
+ expect(user).toStrictEqual(JSONUser);
25
+
26
+ memory.remove(key);
27
+ const removedUser = memory.get(key);
28
+ expect(removedUser).toStrictEqual(null);
29
+ });
30
+
31
+ it('fail get', () => {
32
+ const key = 'NOT_EXISTING_USER_KEY';
33
+
34
+ const user = memory.get(key);
35
+ expect(user).toStrictEqual(null);
36
+ });
37
+ });
@@ -0,0 +1,82 @@
1
+ [
2
+ {
3
+ "id": "0",
4
+ "author": "Alejandro Escamilla",
5
+ "width": 5616,
6
+ "height": 3744,
7
+ "url": "https://unsplash.com/photos/yC-Yzbqy7PY",
8
+ "download_url": "https://picsum.photos/id/0/5616/3744"
9
+ },
10
+ {
11
+ "id": "1",
12
+ "author": "Alejandro Escamilla",
13
+ "width": 5616,
14
+ "height": 3744,
15
+ "url": "https://unsplash.com/photos/LNRyGwIJr5c",
16
+ "download_url": "https://picsum.photos/id/1/5616/3744"
17
+ },
18
+ {
19
+ "id": "10",
20
+ "author": "Paul Jarvis",
21
+ "width": 2500,
22
+ "height": 1667,
23
+ "url": "https://unsplash.com/photos/6J--NXulQCs",
24
+ "download_url": "https://picsum.photos/id/10/2500/1667"
25
+ },
26
+ {
27
+ "id": "100",
28
+ "author": "Tina Rataj",
29
+ "width": 2500,
30
+ "height": 1656,
31
+ "url": "https://unsplash.com/photos/pwaaqfoMibI",
32
+ "download_url": "https://picsum.photos/id/100/2500/1656"
33
+ },
34
+ {
35
+ "id": "1000",
36
+ "author": "Lukas Budimaier",
37
+ "width": 5626,
38
+ "height": 3635,
39
+ "url": "https://unsplash.com/photos/6cY-FvMlmkQ",
40
+ "download_url": "https://picsum.photos/id/1000/5626/3635"
41
+ },
42
+ {
43
+ "id": "1001",
44
+ "author": "Danielle MacInnes",
45
+ "width": 5616,
46
+ "height": 3744,
47
+ "url": "https://unsplash.com/photos/1DkWWN1dr-s",
48
+ "download_url": "https://picsum.photos/id/1001/5616/3744"
49
+ },
50
+ {
51
+ "id": "1002",
52
+ "author": "NASA",
53
+ "width": 4312,
54
+ "height": 2868,
55
+ "url": "https://unsplash.com/photos/6-jTZysYY_U",
56
+ "download_url": "https://picsum.photos/id/1002/4312/2868"
57
+ },
58
+ {
59
+ "id": "1003",
60
+ "author": "E+N Photographies",
61
+ "width": 1181,
62
+ "height": 1772,
63
+ "url": "https://unsplash.com/photos/GYumuBnTqKc",
64
+ "download_url": "https://picsum.photos/id/1003/1181/1772"
65
+ },
66
+ {
67
+ "id": "1004",
68
+ "author": "Greg Rakozy",
69
+ "width": 5616,
70
+ "height": 3744,
71
+ "url": "https://unsplash.com/photos/SSxIGsySh8o",
72
+ "download_url": "https://picsum.photos/id/1004/5616/3744"
73
+ },
74
+ {
75
+ "id": "1005",
76
+ "author": "Matthew Wiebe",
77
+ "width": 5760,
78
+ "height": 3840,
79
+ "url": "https://unsplash.com/photos/tBtuxtLvAZs",
80
+ "download_url": "https://picsum.photos/id/1005/5760/3840"
81
+ }
82
+ ]
@@ -0,0 +1,3 @@
1
+ {
2
+ "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
3
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "id": 123,
3
+ "email": "kekek@awxwax.com",
4
+ "first_name": "Kek",
5
+ "last_name": "Lol",
6
+ "avatar": "https://reqres.in/img/faces/2-image.jpg"
7
+ }
@@ -1,6 +1,6 @@
1
1
  type AnyClass<T> = abstract new (...args: never[]) => T;
2
2
 
3
- export class Injector {
3
+ class Injector {
4
4
  private map: Map<Object, InstanceType<typeof Object>> = new Map();
5
5
 
6
6
  public get = <C extends AnyClass<Object>>(key: C) => this.map.get(key) as InstanceType<C>;
@@ -8,7 +8,7 @@ export abstract class Memory {
8
8
  public abstract get: <T>(key: string) => T | null;
9
9
  }
10
10
 
11
- export class MMKVMemory implements Memory {
11
+ export class MMKVMemory extends Memory {
12
12
  private storage: MMKV = new MMKV();
13
13
 
14
14
  public save = <T>(key: string, value: T) => this.storage.set(key, JSON.stringify(value));
@@ -0,0 +1,24 @@
1
+ import {Http} from '@config/http';
2
+ import {Memory} from '@config/memory';
3
+ import {initLogic} from '../init-logic';
4
+ import {injector} from '@config/injector';
5
+ import {AuthRepo} from '../auth/auth.repo';
6
+ import {AuthService} from '../auth/auth.service';
7
+
8
+ describe('init-logic', () => {
9
+ beforeAll(() => {
10
+ jest.spyOn(AuthService.prototype, 'authInit');
11
+ });
12
+
13
+ it('check init-logic', async () => {
14
+ initLogic();
15
+
16
+ expect(injector.get(Http)).toBeInstanceOf(Http);
17
+
18
+ expect(injector.get(Memory)).toBeInstanceOf(Memory);
19
+ expect(injector.get(AuthRepo)).toBeInstanceOf(AuthRepo);
20
+ expect(injector.get(AuthService)).toBeInstanceOf(AuthService);
21
+
22
+ expect(injector.get(AuthService).authInit).toHaveBeenCalled();
23
+ });
24
+ });
@@ -0,0 +1,49 @@
1
+ import {Http} from '@config/http';
2
+ import {injector} from '@config/injector';
3
+ import {AuthRepoImpl} from '../auth.repo.impl';
4
+ import {MMKVMemory, Memory} from '@config/memory';
5
+
6
+ describe('AuthRepo', () => {
7
+ let http: Http;
8
+ let memory: Memory;
9
+
10
+ let authRepo: AuthRepoImpl;
11
+
12
+ beforeEach(() => {
13
+ http = new Http();
14
+ memory = new MMKVMemory();
15
+
16
+ injector.set(Http, http);
17
+ injector.set(Memory, memory);
18
+
19
+ authRepo = new AuthRepoImpl();
20
+ });
21
+
22
+ it('success loginFromMemory', () => {
23
+ const expectedToken = 'SomeTokenMock';
24
+
25
+ jest.spyOn(memory, 'get').mockImplementationOnce(() => expectedToken);
26
+ jest.spyOn(http, 'addBearerToken');
27
+ const res = authRepo.loginFromMemory();
28
+
29
+ expect(res).toStrictEqual(expectedToken);
30
+ expect(http.addBearerToken).toHaveBeenCalled();
31
+ });
32
+
33
+ it('success/no user in memory, loginFromMemory', () => {
34
+ jest.spyOn(memory, 'get').mockImplementationOnce(() => null);
35
+ jest.spyOn(http, 'addBearerToken');
36
+ const res = authRepo.loginFromMemory();
37
+
38
+ expect(res).toStrictEqual(null);
39
+ expect(http.addBearerToken).not.toHaveBeenCalled();
40
+ });
41
+
42
+ it('login', async () => {
43
+ await expect(authRepo.login('', '')).rejects.toThrow(Error);
44
+ });
45
+
46
+ it('logout', async () => {
47
+ await expect(authRepo.logout()).rejects.toThrow(Error);
48
+ });
49
+ });
@@ -0,0 +1,59 @@
1
+ import {Http} from '@config/http';
2
+ import {injector} from '@config/injector';
3
+ import {AuthService} from '../auth.service';
4
+ import {AuthRepoImpl} from '../auth.repo.impl';
5
+ import {MMKVMemory, Memory} from '@config/memory';
6
+ import {AuthRepo} from '../auth.repo';
7
+
8
+ describe('AuthService', () => {
9
+ let http: Http;
10
+ let memory: Memory;
11
+ let authRepo: AuthRepoImpl;
12
+
13
+ let authService: AuthService;
14
+
15
+ beforeEach(() => {
16
+ http = new Http();
17
+ memory = new MMKVMemory();
18
+
19
+ injector.set(Http, http);
20
+ injector.set(Memory, memory);
21
+
22
+ authRepo = new AuthRepoImpl();
23
+ injector.set(AuthRepo, authRepo);
24
+
25
+ authService = new AuthService();
26
+ });
27
+
28
+ it('success/no user authInit', () => {
29
+ authService.authInit();
30
+
31
+ expect(authService.token).toBeNull();
32
+ });
33
+
34
+ it('success authInit', () => {
35
+ const expectedToken = 'SomeTokenMock';
36
+
37
+ jest.spyOn(authRepo, 'loginFromMemory').mockImplementationOnce(() => expectedToken);
38
+ authService.authInit();
39
+
40
+ expect(authService.token).toStrictEqual(expectedToken);
41
+ });
42
+
43
+ it('success login', async () => {
44
+ const expectedToken = 'SomeTokenMock';
45
+
46
+ jest.spyOn(authRepo, 'login').mockImplementationOnce(() => Promise.resolve(expectedToken));
47
+ await authService.login('', '');
48
+
49
+ expect(authService.token).toStrictEqual(expectedToken);
50
+ });
51
+
52
+ it('exception login', async () => {
53
+ await expect(authService.login('', '')).rejects.toThrow(Error);
54
+ });
55
+
56
+ it('logout', async () => {
57
+ await expect(authService.logout()).rejects.toThrow(Error);
58
+ });
59
+ });
@@ -0,0 +1,42 @@
1
+ import {Http} from '@config/http';
2
+ import {injector} from '@config/injector';
3
+ import {AuthService} from '../auth.service';
4
+ import {AuthRepoImpl} from '../auth.repo.impl';
5
+ import {MMKVMemory, Memory} from '@config/memory';
6
+ import {AuthRepo} from '../auth.repo';
7
+ import {LoginController} from '../screens/login/login.controller';
8
+
9
+ describe('AuthService', () => {
10
+ let http: Http;
11
+ let memory: Memory;
12
+
13
+ let authRepo: AuthRepoImpl;
14
+
15
+ let authService: AuthService;
16
+
17
+ let controller: LoginController;
18
+
19
+ beforeEach(() => {
20
+ http = new Http();
21
+ memory = new MMKVMemory();
22
+
23
+ injector.set(Http, http);
24
+ injector.set(Memory, memory);
25
+
26
+ authRepo = new AuthRepoImpl();
27
+ injector.set(AuthRepo, authRepo);
28
+
29
+ authService = new AuthService();
30
+ injector.set(AuthService, authService);
31
+
32
+ controller = new LoginController();
33
+ });
34
+
35
+ it('login', async () => {
36
+ expect(controller.isProcessing).toBe(false);
37
+
38
+ await expect(controller.login('', '')).rejects.toThrow(Error);
39
+
40
+ expect(controller.isProcessing).toBe(false);
41
+ });
42
+ });
@@ -3,7 +3,7 @@ import {Memory} from '@config/memory';
3
3
  import {Http} from '@config/http';
4
4
  import {AuthRepo} from './auth.repo';
5
5
 
6
- export class AuthRepoImpl implements AuthRepo {
6
+ export class AuthRepoImpl extends AuthRepo {
7
7
  private static readonly TokenKey: string = 'USER_TOKEN';
8
8
 
9
9
  private memory: Memory = injector.get(Memory);
@@ -19,7 +19,7 @@ export class AuthRepoImpl implements AuthRepo {
19
19
  return token;
20
20
  };
21
21
 
22
- public login = async (username: string, password: string) => {
22
+ public login = async (username: string, password: string): Promise<string> => {
23
23
  throw new Error(`Not implemented login with: ${username} ${password}`);
24
24
  };
25
25
 
@@ -1,5 +1,5 @@
1
1
  export abstract class AuthRepo {
2
2
  public abstract loginFromMemory: () => string | null;
3
- public abstract login: (username: string, password: string) => Promise<null>;
3
+ public abstract login: (username: string, password: string) => Promise<string>;
4
4
  public abstract logout: () => Promise<null>;
5
5
  }
@@ -11,7 +11,7 @@ export class AuthService {
11
11
  }
12
12
 
13
13
  public authInit() {
14
- this.authRepo.loginFromMemory();
14
+ this.userToken = this.authRepo.loginFromMemory();
15
15
  }
16
16
 
17
17
  public async login(username: string, password: string) {
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import * as yup from 'yup';
3
- import {yupResolver} from '@hookform/resolvers/yup';
4
3
  import {useLocalObservable} from 'mobx-react-lite';
4
+ import {yupResolver} from '@hookform/resolvers/yup';
5
5
  import {View, Text, StyleSheet} from 'react-native';
6
6
  import {CommonActions} from '@react-navigation/native';
7
7
  import {useForm, Controller, ControllerProps} from 'react-hook-form';