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 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 style={{ flexDirection: "row", justifyContent: "flex-end" }}>
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) => !filter || JSON.stringify(a).toLowerCase().includes(filter)
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={({ section: { data } }) => {
172
- const item = data[0];
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 originalFetch = global.fetch;
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: response,
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
- throw error;
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
- // receiveResponse({
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.use(
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
- // response.data = { message: 'Random overridden Axios response' };
231
+ // response.data = { message: 'Random overridden Axios response' };
232
+ // response.status = 200;
168
233
  // }
169
- receiveResponse({
170
- config: response.config,
171
- data: response.data,
172
- status: response.status,
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 "react-native-in-app-debugger/Libs";
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((d) => {
93
- if (d) {
94
- setBlacklists(JSON.parse(d));
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 [tab, setTab] = useState("api");
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
- <SafeAreaView style={{ flex: 1 }}>
188
- <View style={styles.labelContainer}>
189
- {displayLabels.map((l) => (
190
- <Label key={l}>{l}</Label>
191
- ))}
192
- </View>
193
- <View style={{ flexDirection: "row", padding: 5, gap: 6 }}>
194
- <FlatList
195
- style={{flex: 1}}
196
- data={[
197
- "api",
198
- !!variables && "vars",
199
- "deeplink",
200
- "libs",
201
- ...tabs.map((t) => t.title),
202
- ]
203
- .filter(Boolean)}
204
- keyExtractor={(item) => item}
205
- horizontal
206
- showsHorizontalScrollIndicator={false}
207
- renderItem={({item, index})=>{
208
- const isSelected = item === tab;
209
- return (
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(t)}
231
+ onPress={() => setTab(item)}
247
232
  activeOpacity={isSelected ? 1 : 0.7}
248
- key={t}
249
233
  style={{
250
- flex: 1,
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
- {t}
264
- {!i && !!apis.length && <Text> ({apis.length})</Text>}
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
- </View> */}
270
- <X style={{ marginRight: 5 }} onPress={() => setIsOpen(false)} />
271
- </View>
272
- {tab === "vars" && <Variables variables={variables} />}
273
- {tab === "deeplink" && <Deeplink deeplinkPrefix={deeplinkPrefix} onClose={() => setIsOpen(false)} />}
274
- {tab === "libs" && <Libs />}
275
- {tab === "api" && (
276
- <Api
277
- {...{ apis, setBlacklists, blacklists, maxNumOfApiToStore }}
278
- {...restApiInterceptor}
279
- />
280
- )}
281
- {!!CustomTabComponent && <CustomTabComponent />}
282
- </SafeAreaView>
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-in-app-debugger",
3
- "version": "1.0.86",
3
+ "version": "2.0.0",
4
4
  "description": "This library's main usage is to be used by Non-Technical testers during UAT, SIT or any testing phase.",
5
5
  "main": "index.jsx",
6
6
  "scripts": {