react-native-in-app-debugger 1.0.86 → 2.0.0
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/Api/Row.jsx +9 -2
- package/Api/index.jsx +20 -3
- package/{useApiInterceptor.js → Api/useApiInterceptor.js} +95 -24
- package/Highlight.jsx +1 -1
- package/Mock/MockDetails.jsx +225 -0
- package/Mock/MockGroup.jsx +64 -0
- package/Mock/index.jsx +46 -0
- package/index.jsx +96 -89
- package/package.json +1 -1
package/Api/Row.jsx
CHANGED
|
@@ -51,7 +51,15 @@ export default ({ item, filter, wrap, setWrap }) => {
|
|
|
51
51
|
<Tab key={t.value} {...t} />
|
|
52
52
|
))}
|
|
53
53
|
</View>
|
|
54
|
-
<View
|
|
54
|
+
<View
|
|
55
|
+
style={{
|
|
56
|
+
flexDirection: "row",
|
|
57
|
+
justifyContent: "flex-end",
|
|
58
|
+
alignItems: "center",
|
|
59
|
+
padding: 10,
|
|
60
|
+
gap: 10,
|
|
61
|
+
}}
|
|
62
|
+
>
|
|
55
63
|
<TouchableOpacity
|
|
56
64
|
onPress={() => setWrap((v) => !v)}
|
|
57
65
|
style={{
|
|
@@ -59,7 +67,6 @@ export default ({ item, filter, wrap, setWrap }) => {
|
|
|
59
67
|
backgroundColor: wrap ? "white" : undefined,
|
|
60
68
|
borderColor: "white",
|
|
61
69
|
padding: 5,
|
|
62
|
-
margin: 10,
|
|
63
70
|
borderRadius: 10,
|
|
64
71
|
}}
|
|
65
72
|
>
|
package/Api/index.jsx
CHANGED
|
@@ -157,7 +157,9 @@ export default (props) => {
|
|
|
157
157
|
showsVerticalScrollIndicator
|
|
158
158
|
sections={apis
|
|
159
159
|
.filter(
|
|
160
|
-
(a) =>
|
|
160
|
+
(a) =>
|
|
161
|
+
!filter ||
|
|
162
|
+
JSON.stringify(a.request).toLowerCase().includes(filter)
|
|
161
163
|
)
|
|
162
164
|
.filter((a) => !showBookmarkOnly || props.bookmarks[a.id])
|
|
163
165
|
.map((data) => ({ data: [data], id: data.id }))}
|
|
@@ -168,8 +170,11 @@ export default (props) => {
|
|
|
168
170
|
<View style={{ height: 20 }} />
|
|
169
171
|
)
|
|
170
172
|
}
|
|
171
|
-
renderSectionHeader={({
|
|
172
|
-
|
|
173
|
+
renderSectionHeader={({
|
|
174
|
+
section: {
|
|
175
|
+
data: [item],
|
|
176
|
+
},
|
|
177
|
+
}) => {
|
|
173
178
|
const hasResponse = !!item.response;
|
|
174
179
|
|
|
175
180
|
const duration = item.response?.timestamp
|
|
@@ -198,6 +203,10 @@ export default (props) => {
|
|
|
198
203
|
}}
|
|
199
204
|
/>
|
|
200
205
|
<Text selectable style={{ flex: 1, color, marginVertical: 10 }}>
|
|
206
|
+
<Text style={{ color: "#555", fontSize: 8 }}>
|
|
207
|
+
{item.id + "\n"}
|
|
208
|
+
</Text>
|
|
209
|
+
|
|
201
210
|
<Text style={{ opacity: 0.7 }}>
|
|
202
211
|
{item.request.method +
|
|
203
212
|
` (${item.response?.status ?? "no response"})` +
|
|
@@ -266,6 +275,14 @@ export default (props) => {
|
|
|
266
275
|
>
|
|
267
276
|
<BlacklistIcon />
|
|
268
277
|
</TouchableOpacity>
|
|
278
|
+
{item.interface === "axios" && (
|
|
279
|
+
<TouchableOpacity
|
|
280
|
+
onPress={() => props.goToMock(item)}
|
|
281
|
+
style={styles.actionButton}
|
|
282
|
+
>
|
|
283
|
+
<Text style={{ color: "black", fontSize: 10 }}>Mock</Text>
|
|
284
|
+
</TouchableOpacity>
|
|
285
|
+
)}
|
|
269
286
|
</View>
|
|
270
287
|
</View>
|
|
271
288
|
);
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { useEffect, useState } from 'react';
|
|
2
2
|
// import XHRInterceptor from 'react-native/Libraries/Network/XHRInterceptor.js';
|
|
3
3
|
import axios from 'axios';
|
|
4
|
-
const overrideResponse = false; // Set to true to override the response with a random value
|
|
5
|
-
const filterNonBusinessRelatedAPI = true;
|
|
4
|
+
// const overrideResponse = false; // Set to true to override the response with a random value
|
|
5
|
+
// const filterNonBusinessRelatedAPI = true;
|
|
6
|
+
|
|
7
|
+
const originalFetch = global.fetch;
|
|
6
8
|
|
|
7
9
|
const shouldExclude = (url, method) =>
|
|
8
10
|
['HEAD'].includes(method) ||
|
|
@@ -17,8 +19,8 @@ const parse = (data) => {
|
|
|
17
19
|
return data;
|
|
18
20
|
}
|
|
19
21
|
};
|
|
20
|
-
|
|
21
|
-
export default (maxNumOfApiToStore, blacklists, blacklistRef) => {
|
|
22
|
+
let interceptorId = null;
|
|
23
|
+
export default ({ maxNumOfApiToStore, blacklists, blacklistRef, mocks }) => {
|
|
22
24
|
const [apis, setApis] = useState([]);
|
|
23
25
|
const [bookmarks, setBookmarks] = useState({});
|
|
24
26
|
|
|
@@ -45,11 +47,14 @@ export default (maxNumOfApiToStore, blacklists, blacklistRef) => {
|
|
|
45
47
|
const request = {
|
|
46
48
|
...data,
|
|
47
49
|
timestamp: performance.now(),
|
|
50
|
+
datetime: new Date().toLocaleString(),
|
|
48
51
|
time: `${hour}:${minute}:${second}`,
|
|
52
|
+
isMocked: undefined,
|
|
53
|
+
interface: undefined
|
|
49
54
|
};
|
|
50
55
|
setApis((v) => {
|
|
51
56
|
const newData = [
|
|
52
|
-
{ request, id: Date.now().toString(36) + Math.random().toString(36) },
|
|
57
|
+
{ request, id: Date.now().toString(36) + Math.random().toString(36), isMocked: !!data.isMocked, interface: data.interface },
|
|
53
58
|
...(maxNumOfApiToStore ? v.slice(0, maxNumOfApiToStore - 1) : v),
|
|
54
59
|
];
|
|
55
60
|
return newData;
|
|
@@ -82,28 +87,79 @@ export default (maxNumOfApiToStore, blacklists, blacklistRef) => {
|
|
|
82
87
|
};
|
|
83
88
|
|
|
84
89
|
useEffect(() => {
|
|
85
|
-
const
|
|
90
|
+
const mocked = (url, method) => {
|
|
91
|
+
// console.log('xxxxxx url method', url, method);
|
|
92
|
+
// console.log('xxxxxx mocks', mocks);
|
|
93
|
+
return mocks.find(
|
|
94
|
+
(m) =>
|
|
95
|
+
m.request.url.toLowerCase() === url.toLowerCase() && m.request.method.toLowerCase() === method.toLowerCase(),
|
|
96
|
+
);
|
|
97
|
+
};
|
|
86
98
|
global.fetch = async function (...args) {
|
|
87
|
-
console.log('[Fetch Request]', args);
|
|
88
|
-
const [url, { headers, method, body }] = args;
|
|
99
|
+
// console.log('xxxxxxxx [Fetch Request]', args);
|
|
100
|
+
const [url, { headers, method, body } = { method: 'get' }] = args;
|
|
89
101
|
|
|
90
102
|
try {
|
|
91
103
|
if (!shouldExclude(url, method)) {
|
|
92
|
-
const data = body ? parse(body): undefined;
|
|
104
|
+
const data = body ? parse(body) : undefined;
|
|
105
|
+
|
|
93
106
|
makeRequest({
|
|
94
107
|
url,
|
|
95
108
|
headers,
|
|
96
109
|
method,
|
|
97
110
|
data,
|
|
111
|
+
interface: 'fetch',
|
|
98
112
|
});
|
|
99
113
|
}
|
|
114
|
+
|
|
115
|
+
// return new Response(JSON.stringify({"xxxx": "xxxxx"}), {
|
|
116
|
+
// status: 444,
|
|
117
|
+
// headers: { 'Content-Type': 'application/json' },
|
|
118
|
+
// });
|
|
119
|
+
|
|
100
120
|
const response = await originalFetch(...args);
|
|
121
|
+
// console.log('xxxxxx [Fetch Response]', response);
|
|
122
|
+
// const data = await response.json();
|
|
123
|
+
// alert('aaaa')
|
|
124
|
+
// const returnedTarget = Object.assign({}, response);
|
|
125
|
+
// const data = await returnedTarget.json();
|
|
126
|
+
|
|
127
|
+
// const reader = response.body.getReader();
|
|
128
|
+
// const decoder = new TextDecoder();
|
|
129
|
+
// let result = '';
|
|
130
|
+
|
|
131
|
+
// function read() {
|
|
132
|
+
// return reader.read().then(({ done, value }) => {
|
|
133
|
+
// if (done) {
|
|
134
|
+
// console.log('Response body:', result);
|
|
135
|
+
// return;
|
|
136
|
+
// }
|
|
137
|
+
// result += decoder.decode(value, { stream: true });
|
|
138
|
+
// return read();
|
|
139
|
+
// });
|
|
140
|
+
// }
|
|
141
|
+
|
|
142
|
+
// const data = read();
|
|
143
|
+
// alert(JSON.stringify(data));
|
|
101
144
|
|
|
102
145
|
receiveResponse({
|
|
103
|
-
config:
|
|
146
|
+
config: {
|
|
147
|
+
url: response.url,
|
|
148
|
+
headers: response.headers,
|
|
149
|
+
method: response.method || method,
|
|
150
|
+
},
|
|
104
151
|
status: response.status,
|
|
152
|
+
// data: { aaa: 'bbb' },
|
|
105
153
|
});
|
|
106
154
|
|
|
155
|
+
// console.log('xxxxxx fetch checking mock');
|
|
156
|
+
const mock = mocked(url, method);
|
|
157
|
+
|
|
158
|
+
if (mock) {
|
|
159
|
+
// console.log('xxxxxx jest mocked', mock);
|
|
160
|
+
}
|
|
161
|
+
// console.log('xxxxxx jest response', response);
|
|
162
|
+
|
|
107
163
|
// console.log('[Fetch Response]', args);
|
|
108
164
|
|
|
109
165
|
// if (overrideResponse) {
|
|
@@ -121,7 +177,11 @@ export default (maxNumOfApiToStore, blacklists, blacklistRef) => {
|
|
|
121
177
|
} catch (error) {
|
|
122
178
|
// console.log(error);
|
|
123
179
|
// console.error('[Fetch Error]', error);
|
|
124
|
-
|
|
180
|
+
return new Response(JSON.stringify(error), {
|
|
181
|
+
status: 400,
|
|
182
|
+
headers: { 'Content-Type': 'application/json' },
|
|
183
|
+
});
|
|
184
|
+
// throw error;
|
|
125
185
|
}
|
|
126
186
|
};
|
|
127
187
|
|
|
@@ -133,11 +193,16 @@ export default (maxNumOfApiToStore, blacklists, blacklistRef) => {
|
|
|
133
193
|
const { method, url, headers } = config;
|
|
134
194
|
if (!shouldExclude(url, method)) {
|
|
135
195
|
const data = config.data ? parse(config.data) : undefined;
|
|
196
|
+
|
|
197
|
+
const mock = mocked(url, method);
|
|
198
|
+
|
|
136
199
|
makeRequest({
|
|
137
200
|
url,
|
|
138
201
|
headers,
|
|
139
202
|
method,
|
|
140
203
|
data,
|
|
204
|
+
isMocked: !!mock,
|
|
205
|
+
interface: 'axios',
|
|
141
206
|
});
|
|
142
207
|
}
|
|
143
208
|
|
|
@@ -145,11 +210,8 @@ export default (maxNumOfApiToStore, blacklists, blacklistRef) => {
|
|
|
145
210
|
},
|
|
146
211
|
(error) => {
|
|
147
212
|
// alert(JSON.stringify(error));
|
|
148
|
-
console.error('[Axios Request Error]', error);
|
|
149
|
-
|
|
150
|
-
// config: error.config,
|
|
151
|
-
// status: error.response?.status,
|
|
152
|
-
// });
|
|
213
|
+
// console.error('[Axios Request Error]', error);
|
|
214
|
+
|
|
153
215
|
receiveResponse({
|
|
154
216
|
config: error.config,
|
|
155
217
|
status: error.response?.status,
|
|
@@ -158,19 +220,28 @@ export default (maxNumOfApiToStore, blacklists, blacklistRef) => {
|
|
|
158
220
|
},
|
|
159
221
|
);
|
|
160
222
|
|
|
161
|
-
axios.interceptors.response.
|
|
223
|
+
if (interceptorId) axios.interceptors.response.eject(interceptorId);
|
|
224
|
+
|
|
225
|
+
interceptorId = axios.interceptors.response.use(
|
|
162
226
|
(response) => {
|
|
163
227
|
// console.log('[Axios Response]', response);
|
|
164
228
|
|
|
165
229
|
// if (overrideResponse) {
|
|
166
230
|
// // Replace response.data with random value
|
|
167
|
-
//
|
|
231
|
+
// response.data = { message: 'Random overridden Axios response' };
|
|
232
|
+
// response.status = 200;
|
|
168
233
|
// }
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
234
|
+
// mocked(response.config.url, response.config.method)?.response?.data
|
|
235
|
+
|
|
236
|
+
// console.log('xxxxxx axios checking mock');
|
|
237
|
+
const mock = mocked(response.config.url, response.config.method);
|
|
238
|
+
|
|
239
|
+
if (mock?.response) {
|
|
240
|
+
// console.log('xxxxxx axios mocked', mock);
|
|
241
|
+
response = mock.response;
|
|
242
|
+
}
|
|
243
|
+
// console.log('xxxxxx axios response', response);
|
|
244
|
+
receiveResponse(response);
|
|
174
245
|
|
|
175
246
|
return response;
|
|
176
247
|
},
|
|
@@ -182,7 +253,7 @@ export default (maxNumOfApiToStore, blacklists, blacklistRef) => {
|
|
|
182
253
|
return Promise.reject(error);
|
|
183
254
|
},
|
|
184
255
|
);
|
|
185
|
-
}, []);
|
|
256
|
+
}, [mocks]);
|
|
186
257
|
|
|
187
258
|
return { apis, clear: () => setApis([]), bookmarks, setBookmarks };
|
|
188
259
|
};
|
package/Highlight.jsx
CHANGED
|
@@ -3,7 +3,7 @@ import Text from './Text';
|
|
|
3
3
|
|
|
4
4
|
export default ({ text, filter, style = {} }) => {
|
|
5
5
|
if (!filter || !/^[a-zA-Z0-9./]+$/.test(filter)) return <Text style={style}>{text}</Text>;
|
|
6
|
-
const indices = [...text.matchAll(new RegExp(filter, 'gi'))].map((a) => a.index);
|
|
6
|
+
const indices = text ? [...text.matchAll(new RegExp(filter, 'gi'))].map((a) => a.index) : [];
|
|
7
7
|
if (!indices.length) return <Text style={style}>{text}</Text>;
|
|
8
8
|
return (
|
|
9
9
|
<>
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import React, { use, useEffect, useState } from 'react';
|
|
2
|
+
import { View, StyleSheet, TextInput, TouchableOpacity, Alert } from 'react-native';
|
|
3
|
+
import Text from '../Text';
|
|
4
|
+
import X from '../X';
|
|
5
|
+
|
|
6
|
+
const removeId = (key, value) => (key === 'id' ? undefined : value);
|
|
7
|
+
export default (p) => {
|
|
8
|
+
const [tab, setTab] = useState('response');
|
|
9
|
+
const [tmpResBody, setTmpResBody] = useState('');
|
|
10
|
+
const [tmpResStatus, setTmpResStatus] = useState('');
|
|
11
|
+
const [tmpMockDetails, setTmpMockDetails] = useState({});
|
|
12
|
+
|
|
13
|
+
const save = (cb) => {
|
|
14
|
+
if (tab === 'response') {
|
|
15
|
+
try {
|
|
16
|
+
if (!tmpResStatus) {
|
|
17
|
+
return Alert.alert('Sorry', 'Status code is required');
|
|
18
|
+
}
|
|
19
|
+
const tmp = JSON.parse(tmpMockDetails);
|
|
20
|
+
tmp.response.data = JSON.parse(tmpResBody);
|
|
21
|
+
tmp.response.status = parseInt(tmpResStatus, 10);
|
|
22
|
+
p.setMockDetails(tmp);
|
|
23
|
+
|
|
24
|
+
p.setMocks((v) => {
|
|
25
|
+
const index = v.findIndex((m) => m.id === p.mockDetails.id);
|
|
26
|
+
// p.setMockDetails(null);
|
|
27
|
+
if (!~index) {
|
|
28
|
+
const id = Date.now().toString(36) + Math.random().toString(36);
|
|
29
|
+
p.setMockDetails((md) => ({ ...md, id }));
|
|
30
|
+
return [...v, { ...tmp, id }];
|
|
31
|
+
}
|
|
32
|
+
v[index] = { ...v[index], ...tmp };
|
|
33
|
+
p.setMockDetails(v[index]);
|
|
34
|
+
return v;
|
|
35
|
+
});
|
|
36
|
+
// p.setMocks(v => {
|
|
37
|
+
// const tmp = [...v];
|
|
38
|
+
// for(let i = 0; i < tmp.length; i++) {
|
|
39
|
+
// if (tmp[i].id === p.mockDetails.id) {
|
|
40
|
+
// tmp[i] = { ...tmp[i], ...tmp };
|
|
41
|
+
// break;
|
|
42
|
+
// }
|
|
43
|
+
// }
|
|
44
|
+
// // v.forEach(m => m.id === p.mockDetails.id && (m.response = tmp.response));
|
|
45
|
+
// return tmp;
|
|
46
|
+
// })
|
|
47
|
+
cb?.();
|
|
48
|
+
} catch (e) {
|
|
49
|
+
return Alert.alert('Sorry', 'Please fix the response body JSON');
|
|
50
|
+
}
|
|
51
|
+
} else if (tab === 'json') {
|
|
52
|
+
cb?.();
|
|
53
|
+
} else if (tab === 'request') {
|
|
54
|
+
cb?.();
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
const reset = () => {
|
|
58
|
+
if (!p.mockDetails) return;
|
|
59
|
+
setTmpMockDetails(JSON.stringify(p.mockDetails, removeId, 4));
|
|
60
|
+
setTmpResBody(JSON.stringify(p.mockDetails.response.data, removeId, 4));
|
|
61
|
+
setTmpResStatus(p.mockDetails.response.status + '' || '');
|
|
62
|
+
};
|
|
63
|
+
useEffect(reset, [p.mockDetails]);
|
|
64
|
+
const [canReset, setCanReset] = useState(false);
|
|
65
|
+
|
|
66
|
+
// const parse = (data) => {
|
|
67
|
+
// try {
|
|
68
|
+
// return JSON.parse(data);
|
|
69
|
+
// } catch (e) {
|
|
70
|
+
// // Alert.alert('Error', 'Invalid JSON');
|
|
71
|
+
// return {};
|
|
72
|
+
// }
|
|
73
|
+
// };
|
|
74
|
+
|
|
75
|
+
useEffect(() => {
|
|
76
|
+
try {
|
|
77
|
+
setCanReset(JSON.stringify(p.mockDetails, removeId) != tmpMockDetails);
|
|
78
|
+
} catch (e) {
|
|
79
|
+
setCanReset(true);
|
|
80
|
+
}
|
|
81
|
+
}, [p.mockDetails, tmpMockDetails]);
|
|
82
|
+
if (!p.mockDetails) return null;
|
|
83
|
+
const isnew = !p.mocks.some((m) => m.id === p.mockDetails.id);
|
|
84
|
+
|
|
85
|
+
return (
|
|
86
|
+
<View
|
|
87
|
+
style={{
|
|
88
|
+
top: 0,
|
|
89
|
+
bottom: 0,
|
|
90
|
+
left: 0,
|
|
91
|
+
right: 0,
|
|
92
|
+
position: 'absolute',
|
|
93
|
+
zIndex: 1000,
|
|
94
|
+
backgroundColor: 'grey',
|
|
95
|
+
width: '100%',
|
|
96
|
+
height: '100%',
|
|
97
|
+
paddingTop: 50,
|
|
98
|
+
}}
|
|
99
|
+
>
|
|
100
|
+
{!isnew && <Text style={{ opacity: 0.3 }}>{p.mockDetails.id}</Text>}
|
|
101
|
+
<View style={{ flexDirection: 'row' }}>
|
|
102
|
+
<Text style={{ flex: 1 }}>{`(${p.mockDetails.request.method}) ${p.mockDetails.request.url}`}</Text>
|
|
103
|
+
<TouchableOpacity onPress={() => p.setMockDetails(null)}>
|
|
104
|
+
<X size={25} />
|
|
105
|
+
</TouchableOpacity>
|
|
106
|
+
</View>
|
|
107
|
+
<View
|
|
108
|
+
style={{
|
|
109
|
+
marginVertical: 10,
|
|
110
|
+
flexDirection: 'row',
|
|
111
|
+
justifyContent: 'space-around',
|
|
112
|
+
backgroundColor: 'black',
|
|
113
|
+
borderRadius: 5,
|
|
114
|
+
padding: 5,
|
|
115
|
+
}}
|
|
116
|
+
>
|
|
117
|
+
{['request', 'response', 'json'].map((item) => {
|
|
118
|
+
const isSelected = item === tab;
|
|
119
|
+
return (
|
|
120
|
+
<TouchableOpacity
|
|
121
|
+
style={{ backgroundColor: isSelected ? 'white' : 'black', padding: 3, borderRadius: 5 }}
|
|
122
|
+
onPress={() => {
|
|
123
|
+
save(() => setTab(item));
|
|
124
|
+
}}
|
|
125
|
+
>
|
|
126
|
+
<Text style={{ textTransform: 'uppercase', color: isSelected ? 'black' : 'white' }}>{item}</Text>
|
|
127
|
+
</TouchableOpacity>
|
|
128
|
+
);
|
|
129
|
+
})}
|
|
130
|
+
</View>
|
|
131
|
+
{tab === 'response' && (
|
|
132
|
+
<View style={{ gap: 3 }}>
|
|
133
|
+
<View style={{ flexDirection: 'row', gap: 5 }}>
|
|
134
|
+
<Text>Status</Text>
|
|
135
|
+
<TextInput
|
|
136
|
+
style={{ backgroundColor: '#ccc', width: 50, padding: 2 }}
|
|
137
|
+
inputMode='numeric'
|
|
138
|
+
onChangeText={(t) => {
|
|
139
|
+
setTmpResStatus(t);
|
|
140
|
+
// try {
|
|
141
|
+
// const newData = JSON.parse(tmpMockDetails);
|
|
142
|
+
// newData.response.status = parseInt(t, 10);
|
|
143
|
+
// setTmpMockDetails(JSON.stringify(newData, removeId, 4));
|
|
144
|
+
// } catch (e) {
|
|
145
|
+
// Alert.alert('Error', 'Invalid JSON');
|
|
146
|
+
// }
|
|
147
|
+
}}
|
|
148
|
+
value={tmpResStatus}
|
|
149
|
+
/>
|
|
150
|
+
</View>
|
|
151
|
+
|
|
152
|
+
<View style={{ flexDirection: 'row', gap: 5 }}>
|
|
153
|
+
<Text>Body</Text>
|
|
154
|
+
<TextInput
|
|
155
|
+
style={{ backgroundColor: '#ccc', height: 250, flex: 1, padding: 2 }}
|
|
156
|
+
multiline
|
|
157
|
+
onChangeText={setTmpResBody}
|
|
158
|
+
value={tmpResBody}
|
|
159
|
+
/>
|
|
160
|
+
</View>
|
|
161
|
+
</View>
|
|
162
|
+
)}
|
|
163
|
+
{tab !== 'response' && <Text style={{ marginTop: 30, textAlign: 'center' }}>To be developed later</Text>}
|
|
164
|
+
{/* {tab === 'json' && (
|
|
165
|
+
<TextInput onChangeText={setTmpMockDetails} value={tmpMockDetails} style={{ height: '40%' }} multiline />
|
|
166
|
+
)} */}
|
|
167
|
+
{tab === 'response' && (
|
|
168
|
+
<View style={{ flexDirection: 'row', justifyContent: 'space-between', padding: 10 }}>
|
|
169
|
+
{!isnew && (
|
|
170
|
+
<TouchableOpacity
|
|
171
|
+
style={styles.actionButton}
|
|
172
|
+
onPress={() => {
|
|
173
|
+
p.deleteMock(p.mockDetails.id);
|
|
174
|
+
p.setMockDetails(null);
|
|
175
|
+
}}
|
|
176
|
+
>
|
|
177
|
+
<Text style={{ fontSize: 15 }}>Delete</Text>
|
|
178
|
+
</TouchableOpacity>
|
|
179
|
+
)}
|
|
180
|
+
{canReset && (
|
|
181
|
+
<TouchableOpacity style={styles.actionButton} onPress={reset}>
|
|
182
|
+
<Text style={{ fontSize: 15 }}>Reset</Text>
|
|
183
|
+
</TouchableOpacity>
|
|
184
|
+
)}
|
|
185
|
+
{canReset && (
|
|
186
|
+
<TouchableOpacity
|
|
187
|
+
style={styles.actionButton}
|
|
188
|
+
onPress={() => {
|
|
189
|
+
// try {
|
|
190
|
+
// const tmp = JSON.parse(tmpMockDetails);
|
|
191
|
+
// if (!tmp.request) {
|
|
192
|
+
// Alert.alert('Error', 'Invalid JSON');
|
|
193
|
+
// return;
|
|
194
|
+
// }
|
|
195
|
+
// p.setMocks((v) => {
|
|
196
|
+
// const index = v.findIndex((m) => m.id === p.mockDetails.id);
|
|
197
|
+
// p.setMockDetails(null);
|
|
198
|
+
// if (!~index) return [...v, { ...tmp, id: Date.now().toString(36) + Math.random().toString(36) }];
|
|
199
|
+
// v[index] = { ...tmp, id: p.mockDetails.id };
|
|
200
|
+
// return v;
|
|
201
|
+
// });
|
|
202
|
+
// } catch (e) {
|
|
203
|
+
// Alert.alert('Error', 'Invalid JSON');
|
|
204
|
+
// }
|
|
205
|
+
save();
|
|
206
|
+
}}
|
|
207
|
+
>
|
|
208
|
+
<Text style={{ fontSize: 15 }}>Save</Text>
|
|
209
|
+
</TouchableOpacity>
|
|
210
|
+
)}
|
|
211
|
+
</View>
|
|
212
|
+
)}
|
|
213
|
+
</View>
|
|
214
|
+
);
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
const styles = StyleSheet.create({
|
|
218
|
+
actionButton: {
|
|
219
|
+
backgroundColor: 'white',
|
|
220
|
+
borderRadius: 5,
|
|
221
|
+
padding: 4,
|
|
222
|
+
alignItems: 'center',
|
|
223
|
+
justifyContent: 'center',
|
|
224
|
+
},
|
|
225
|
+
});
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { StyleSheet, TouchableHighlight, TouchableOpacity, View } from 'react-native';
|
|
3
|
+
import Text from '../Text';
|
|
4
|
+
import Highlight from '../Highlight';
|
|
5
|
+
|
|
6
|
+
export const MAX_URL_LENGTH = 100;
|
|
7
|
+
|
|
8
|
+
export default (p) => {
|
|
9
|
+
const [expand, setExpand] = useState(false);
|
|
10
|
+
return (
|
|
11
|
+
<View>
|
|
12
|
+
<TouchableHighlight underlayColor='#ffffff44' onPress={() => setExpand((v) => !v)}>
|
|
13
|
+
<View
|
|
14
|
+
style={{
|
|
15
|
+
flexDirection: 'row',
|
|
16
|
+
justifyContent: 'space-between',
|
|
17
|
+
alignItems: 'center',
|
|
18
|
+
padding: 5,
|
|
19
|
+
flex: 1,
|
|
20
|
+
}}
|
|
21
|
+
>
|
|
22
|
+
<Text selectable style={{ color: 'white', marginVertical: 10 }}>
|
|
23
|
+
<Highlight
|
|
24
|
+
text={p.item.title.slice(0, MAX_URL_LENGTH) + (p.item.title.length > MAX_URL_LENGTH ? '.....' : '')}
|
|
25
|
+
filter={p.filter}
|
|
26
|
+
/>
|
|
27
|
+
</Text>
|
|
28
|
+
<View>
|
|
29
|
+
<View style={{ padding: 5, backgroundColor: '#777', borderRadius: 8 }}>
|
|
30
|
+
<Text>{p.item.data.length}</Text>
|
|
31
|
+
</View>
|
|
32
|
+
</View>
|
|
33
|
+
</View>
|
|
34
|
+
</TouchableHighlight>
|
|
35
|
+
{expand &&
|
|
36
|
+
p.item.data.map((d) => (
|
|
37
|
+
<View style={{padding: 5}} key={d.id}>
|
|
38
|
+
<Text style={{ color: '#555', fontSize: 8 }}>{d.id}</Text>
|
|
39
|
+
<View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
|
|
40
|
+
<Text style={{ color: 'grey' }}>{d.request.datetime}</Text>
|
|
41
|
+
<View style={{ flexDirection: 'row', gap: 5 }}>
|
|
42
|
+
<TouchableOpacity style={styles.actionButton} onPress={() => p.deleteMock(d.id)}>
|
|
43
|
+
<Text style={{ color: 'black', fontSize: 10 }}>delete</Text>
|
|
44
|
+
</TouchableOpacity>
|
|
45
|
+
<TouchableOpacity style={styles.actionButton} onPress={() => p.setMockDetails(d)}>
|
|
46
|
+
<Text style={{ color: 'black', fontSize: 10 }}>edit</Text>
|
|
47
|
+
</TouchableOpacity>
|
|
48
|
+
</View>
|
|
49
|
+
</View>
|
|
50
|
+
</View>
|
|
51
|
+
))}
|
|
52
|
+
</View>
|
|
53
|
+
);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const styles = StyleSheet.create({
|
|
57
|
+
actionButton: {
|
|
58
|
+
backgroundColor: 'white',
|
|
59
|
+
borderRadius: 5,
|
|
60
|
+
padding: 4,
|
|
61
|
+
alignItems: 'center',
|
|
62
|
+
justifyContent: 'center',
|
|
63
|
+
},
|
|
64
|
+
});
|
package/Mock/index.jsx
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { FlatList, StyleSheet, TextInput } from 'react-native';
|
|
3
|
+
import MockGroup from './MockGroup';
|
|
4
|
+
|
|
5
|
+
const format = (m) => '(' + m.request.method + ') ' + m.request.url;
|
|
6
|
+
|
|
7
|
+
export default (p) => {
|
|
8
|
+
const [filter, setFilter] = useState('');
|
|
9
|
+
|
|
10
|
+
const data = [];
|
|
11
|
+
p.mocks.forEach((m) => {
|
|
12
|
+
const tmp = format(m);
|
|
13
|
+
if (!data.some((d) => d.title === tmp)) data.push({ title: tmp, data: [m] });
|
|
14
|
+
else data.forEach((d) => d.title === tmp && d.data.push(m));
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<>
|
|
19
|
+
<TextInput
|
|
20
|
+
value={filter}
|
|
21
|
+
placeholder='Filter...'
|
|
22
|
+
placeholderTextColor='grey'
|
|
23
|
+
style={styles.textInput}
|
|
24
|
+
onChangeText={(t) => setFilter(t.toLowerCase())}
|
|
25
|
+
clearButtonMode='always'
|
|
26
|
+
/>
|
|
27
|
+
<FlatList
|
|
28
|
+
contentContainerStyle={{ padding: 5, paddingBottom: 20 }}
|
|
29
|
+
data={data.filter((l) => !filter || l.title.toLowerCase().includes(filter))}
|
|
30
|
+
showsVerticalScrollIndicator
|
|
31
|
+
keyExtractor={(i) => i.title}
|
|
32
|
+
renderItem={(pp) => <MockGroup {...pp} {...p} filter={filter} />}
|
|
33
|
+
/>
|
|
34
|
+
</>
|
|
35
|
+
);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const styles = StyleSheet.create({
|
|
39
|
+
textInput: {
|
|
40
|
+
marginHorizontal: 5,
|
|
41
|
+
padding: 5,
|
|
42
|
+
color: 'white',
|
|
43
|
+
backgroundColor: '#333',
|
|
44
|
+
borderRadius: 8,
|
|
45
|
+
},
|
|
46
|
+
});
|
package/index.jsx
CHANGED
|
@@ -31,9 +31,11 @@ try {
|
|
|
31
31
|
import useAnimation from "./useAnimation";
|
|
32
32
|
import Variables from "./Variables";
|
|
33
33
|
import Api from "./Api";
|
|
34
|
-
import useApiInterceptor from "./useApiInterceptor";
|
|
34
|
+
import useApiInterceptor from "./Api/useApiInterceptor";
|
|
35
35
|
import useStateRef from "./useStateRef";
|
|
36
|
-
import Libs from "
|
|
36
|
+
import Libs from "./Libs";
|
|
37
|
+
import Mock from "./Mock";
|
|
38
|
+
import MockDetails from "./Mock/MockDetails";
|
|
37
39
|
|
|
38
40
|
const fontSize = 7;
|
|
39
41
|
|
|
@@ -57,6 +59,11 @@ const Label = (props) => (
|
|
|
57
59
|
/>
|
|
58
60
|
);
|
|
59
61
|
|
|
62
|
+
let cachedMocks = [];
|
|
63
|
+
LocalStorage.getItem("in-app-debugger-mocked").then((d) => {
|
|
64
|
+
if (d) cachedMocks = JSON.parse(d);
|
|
65
|
+
});
|
|
66
|
+
|
|
60
67
|
export default ({
|
|
61
68
|
variables,
|
|
62
69
|
env,
|
|
@@ -65,7 +72,6 @@ export default ({
|
|
|
65
72
|
labels = [],
|
|
66
73
|
tabs = [],
|
|
67
74
|
}) => {
|
|
68
|
-
|
|
69
75
|
const deeplinkPrefix = variables?.DEEPLINK_PREFIX;
|
|
70
76
|
const [blacklists, setB, blacklistRef] = useStateRef([]);
|
|
71
77
|
const dimension = useWindowDimensions();
|
|
@@ -86,25 +92,37 @@ export default ({
|
|
|
86
92
|
}
|
|
87
93
|
};
|
|
88
94
|
|
|
95
|
+
const [tab, setTab] = useState("api");
|
|
96
|
+
const [mocks, setMocks] = useState(cachedMocks);
|
|
97
|
+
const [mockDetails, setMockDetails] = useState();
|
|
98
|
+
|
|
99
|
+
useEffect(() => {
|
|
100
|
+
LocalStorage?.setItem("in-app-debugger-mocked", JSON.stringify(mocks));
|
|
101
|
+
}, [mocks]);
|
|
102
|
+
|
|
89
103
|
if (LocalStorage) {
|
|
90
104
|
useEffect(() => {
|
|
91
105
|
setTimeout(() => {
|
|
92
|
-
LocalStorage.getItem("in-app-debugger-blacklist").then(
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}
|
|
96
|
-
});
|
|
106
|
+
LocalStorage.getItem("in-app-debugger-blacklist").then(
|
|
107
|
+
(d) => d && setBlacklists(JSON.parse(d))
|
|
108
|
+
);
|
|
97
109
|
}, 4000);
|
|
98
110
|
}, []);
|
|
99
111
|
}
|
|
100
112
|
|
|
101
|
-
const { apis, ...restApiInterceptor } = useApiInterceptor(
|
|
113
|
+
const { apis, ...restApiInterceptor } = useApiInterceptor({
|
|
102
114
|
maxNumOfApiToStore,
|
|
103
115
|
blacklists,
|
|
104
|
-
blacklistRef
|
|
105
|
-
|
|
116
|
+
blacklistRef,
|
|
117
|
+
mocks,
|
|
118
|
+
});
|
|
106
119
|
|
|
107
|
-
const
|
|
120
|
+
const goToMock = (d) => {
|
|
121
|
+
setMockDetails(d);
|
|
122
|
+
//setTab('mock');
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const deleteMock = (id) => setMocks((v) => v.filter((i) => i.id !== id));
|
|
108
126
|
|
|
109
127
|
const errors = apis.filter((a) => a.response?.error).length;
|
|
110
128
|
const numPendingApiCalls = apis.filter((a) => !a.response).length;
|
|
@@ -136,6 +154,7 @@ export default ({
|
|
|
136
154
|
const CustomTabComponent = tabs.find((t) => tab === t.title)?.component;
|
|
137
155
|
const [disapear, setDisapear] = useState();
|
|
138
156
|
if (disapear) return null;
|
|
157
|
+
|
|
139
158
|
return (
|
|
140
159
|
<Animated.View
|
|
141
160
|
style={{
|
|
@@ -184,70 +203,35 @@ export default ({
|
|
|
184
203
|
))}
|
|
185
204
|
</TouchableOpacity>
|
|
186
205
|
) : (
|
|
187
|
-
|
|
188
|
-
<
|
|
189
|
-
{
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
<
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
<TouchableOpacity
|
|
211
|
-
onPress={() => setTab(item)}
|
|
212
|
-
activeOpacity={isSelected ? 1 : 0.7}
|
|
213
|
-
style={{
|
|
214
|
-
paddingHorizontal: 8,
|
|
215
|
-
borderBottomWidth: +isSelected,
|
|
216
|
-
borderColor: "white",
|
|
217
|
-
}}
|
|
218
|
-
>
|
|
219
|
-
<Text
|
|
220
|
-
style={{
|
|
221
|
-
color: "white",
|
|
222
|
-
opacity: isSelected ? 1 : 0.5,
|
|
223
|
-
textAlign: "center",
|
|
224
|
-
textTransform: "uppercase",
|
|
225
|
-
}}
|
|
226
|
-
>
|
|
227
|
-
{item}
|
|
228
|
-
{!index && !!apis.length && <Text> ({apis.length})</Text>}
|
|
229
|
-
</Text>
|
|
230
|
-
</TouchableOpacity>
|
|
231
|
-
);
|
|
232
|
-
}}
|
|
233
|
-
/>
|
|
234
|
-
{/* <View style={{ flex: 1, flexDirection: "row" }}>
|
|
235
|
-
{[
|
|
236
|
-
"api",
|
|
237
|
-
!!variables && "vars",
|
|
238
|
-
"libs",
|
|
239
|
-
...tabs.map((t) => t.title),
|
|
240
|
-
]
|
|
241
|
-
.filter(Boolean)
|
|
242
|
-
.map((t, i) => {
|
|
243
|
-
const isSelected = t === tab;
|
|
206
|
+
<>
|
|
207
|
+
<SafeAreaView style={{ flex: 1 }}>
|
|
208
|
+
<View style={styles.labelContainer}>
|
|
209
|
+
{displayLabels.map((l) => (
|
|
210
|
+
<Label key={l}>{l}</Label>
|
|
211
|
+
))}
|
|
212
|
+
</View>
|
|
213
|
+
<View style={{ flexDirection: "row", padding: 5, gap: 6 }}>
|
|
214
|
+
<FlatList
|
|
215
|
+
style={{ flex: 1 }}
|
|
216
|
+
data={[
|
|
217
|
+
"api",
|
|
218
|
+
"mock",
|
|
219
|
+
!!variables && "vars",
|
|
220
|
+
"deeplink",
|
|
221
|
+
"libs",
|
|
222
|
+
...tabs.map((t) => t.title),
|
|
223
|
+
].filter(Boolean)}
|
|
224
|
+
keyExtractor={(item) => item}
|
|
225
|
+
horizontal
|
|
226
|
+
showsHorizontalScrollIndicator={false}
|
|
227
|
+
renderItem={({ item, index }) => {
|
|
228
|
+
const isSelected = item === tab;
|
|
244
229
|
return (
|
|
245
230
|
<TouchableOpacity
|
|
246
|
-
onPress={() => setTab(
|
|
231
|
+
onPress={() => setTab(item)}
|
|
247
232
|
activeOpacity={isSelected ? 1 : 0.7}
|
|
248
|
-
key={t}
|
|
249
233
|
style={{
|
|
250
|
-
|
|
234
|
+
paddingHorizontal: 8,
|
|
251
235
|
borderBottomWidth: +isSelected,
|
|
252
236
|
borderColor: "white",
|
|
253
237
|
}}
|
|
@@ -260,26 +244,49 @@ export default ({
|
|
|
260
244
|
textTransform: "uppercase",
|
|
261
245
|
}}
|
|
262
246
|
>
|
|
263
|
-
{
|
|
264
|
-
{!
|
|
247
|
+
{item}
|
|
248
|
+
{!index && !!apis.length && (
|
|
249
|
+
<Text> ({apis.length})</Text>
|
|
250
|
+
)}
|
|
251
|
+
{index === 1 && !!mocks.length && (
|
|
252
|
+
<Text> ({mocks.length})</Text>
|
|
253
|
+
)}
|
|
265
254
|
</Text>
|
|
266
255
|
</TouchableOpacity>
|
|
267
256
|
);
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
257
|
+
}}
|
|
258
|
+
/>
|
|
259
|
+
<X style={{ marginRight: 5 }} onPress={() => setIsOpen(false)} />
|
|
260
|
+
</View>
|
|
261
|
+
{tab === "vars" && <Variables variables={variables} />}
|
|
262
|
+
{tab === "mock" && (
|
|
263
|
+
<Mock {...{ mocks, setMocks, deleteMock, setMockDetails }} />
|
|
264
|
+
)}
|
|
265
|
+
{tab === "deeplink" && (
|
|
266
|
+
<Deeplink
|
|
267
|
+
deeplinkPrefix={deeplinkPrefix}
|
|
268
|
+
onClose={() => setIsOpen(false)}
|
|
269
|
+
/>
|
|
270
|
+
)}
|
|
271
|
+
{tab === "libs" && <Libs />}
|
|
272
|
+
{tab === "api" && (
|
|
273
|
+
<Api
|
|
274
|
+
{...{
|
|
275
|
+
apis,
|
|
276
|
+
setBlacklists,
|
|
277
|
+
blacklists,
|
|
278
|
+
maxNumOfApiToStore,
|
|
279
|
+
goToMock,
|
|
280
|
+
}}
|
|
281
|
+
{...restApiInterceptor}
|
|
282
|
+
/>
|
|
283
|
+
)}
|
|
284
|
+
{!!CustomTabComponent && <CustomTabComponent />}
|
|
285
|
+
</SafeAreaView>
|
|
286
|
+
<MockDetails
|
|
287
|
+
{...{ mockDetails, setMockDetails, setMocks, deleteMock, mocks }}
|
|
288
|
+
/>
|
|
289
|
+
</>
|
|
283
290
|
)}
|
|
284
291
|
</Animated.View>
|
|
285
292
|
);
|
package/package.json
CHANGED