yaver-feedback-react-native 0.2.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/README.md +690 -0
- package/app.plugin.js +70 -0
- package/package.json +39 -0
- package/src/BlackBox.ts +317 -0
- package/src/ConnectionScreen.tsx +400 -0
- package/src/Discovery.ts +223 -0
- package/src/FeedbackModal.tsx +678 -0
- package/src/FixReport.tsx +313 -0
- package/src/FloatingButton.tsx +860 -0
- package/src/P2PClient.ts +303 -0
- package/src/ShakeDetector.ts +57 -0
- package/src/YaverFeedback.ts +345 -0
- package/src/__tests__/Discovery.test.ts +187 -0
- package/src/__tests__/P2PClient.test.ts +218 -0
- package/src/__tests__/SDKToken.test.ts +268 -0
- package/src/__tests__/YaverFeedback.test.ts +189 -0
- package/src/__tests__/types.test.ts +247 -0
- package/src/capture.ts +84 -0
- package/src/expo.ts +62 -0
- package/src/index.ts +54 -0
- package/src/types.ts +251 -0
- package/src/upload.ts +74 -0
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
Modal,
|
|
4
|
+
Pressable,
|
|
5
|
+
ScrollView,
|
|
6
|
+
StyleSheet,
|
|
7
|
+
Text,
|
|
8
|
+
TouchableOpacity,
|
|
9
|
+
View,
|
|
10
|
+
} from 'react-native';
|
|
11
|
+
import type { TestFix, TestSession } from './types';
|
|
12
|
+
|
|
13
|
+
export interface FixReportProps {
|
|
14
|
+
/** Test session data with fixes list */
|
|
15
|
+
session: TestSession | null;
|
|
16
|
+
/** Whether the modal is visible */
|
|
17
|
+
visible: boolean;
|
|
18
|
+
/** Called when the user closes the report */
|
|
19
|
+
onClose: () => void;
|
|
20
|
+
/** Accent color (matches FloatingButton) */
|
|
21
|
+
color?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Fix Report viewer — shows a markdown-style list of all fixes
|
|
26
|
+
* applied by the AI agent during a test session.
|
|
27
|
+
*
|
|
28
|
+
* Each fix shows the file, description, and error that triggered it.
|
|
29
|
+
* Tap a fix to expand and see the diff/code snippet.
|
|
30
|
+
*
|
|
31
|
+
* Fixes are NOT committed — they're staged changes the developer
|
|
32
|
+
* can review, accept, or revert.
|
|
33
|
+
*/
|
|
34
|
+
export const FixReport: React.FC<FixReportProps> = ({
|
|
35
|
+
session,
|
|
36
|
+
visible,
|
|
37
|
+
onClose,
|
|
38
|
+
color = '#6366f1',
|
|
39
|
+
}) => {
|
|
40
|
+
const [expanded, setExpanded] = useState<Set<string>>(new Set());
|
|
41
|
+
|
|
42
|
+
const toggleExpand = (id: string) => {
|
|
43
|
+
setExpanded((prev) => {
|
|
44
|
+
const next = new Set(prev);
|
|
45
|
+
if (next.has(id)) next.delete(id);
|
|
46
|
+
else next.add(id);
|
|
47
|
+
return next;
|
|
48
|
+
});
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const fixes = session?.fixes ?? [];
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<Modal visible={visible} animationType="slide" transparent>
|
|
55
|
+
<View style={s.overlay}>
|
|
56
|
+
<View style={s.container}>
|
|
57
|
+
{/* Header */}
|
|
58
|
+
<View style={s.header}>
|
|
59
|
+
<View style={{ flex: 1 }}>
|
|
60
|
+
<Text style={s.title}>Test Report</Text>
|
|
61
|
+
{session && (
|
|
62
|
+
<Text style={s.subtitle}>
|
|
63
|
+
{session.screensTested}/{session.screensDiscovered} screens
|
|
64
|
+
{' \u00B7 '}
|
|
65
|
+
{session.errorsFound} errors
|
|
66
|
+
{' \u00B7 '}
|
|
67
|
+
{fixes.length} fixes
|
|
68
|
+
</Text>
|
|
69
|
+
)}
|
|
70
|
+
</View>
|
|
71
|
+
<TouchableOpacity onPress={onClose} style={s.closeBtn}>
|
|
72
|
+
<Text style={s.closeBtnText}>{'\u2715'}</Text>
|
|
73
|
+
</TouchableOpacity>
|
|
74
|
+
</View>
|
|
75
|
+
|
|
76
|
+
{/* Status bar */}
|
|
77
|
+
{session?.active && (
|
|
78
|
+
<View style={[s.statusBar, { backgroundColor: `${color}20` }]}>
|
|
79
|
+
<View style={[s.statusDot, { backgroundColor: color }]} />
|
|
80
|
+
<Text style={[s.statusText, { color }]}>
|
|
81
|
+
{session.status || 'Testing...'}
|
|
82
|
+
</Text>
|
|
83
|
+
</View>
|
|
84
|
+
)}
|
|
85
|
+
|
|
86
|
+
{/* Fix list */}
|
|
87
|
+
<ScrollView style={s.list} contentContainerStyle={s.listContent}>
|
|
88
|
+
{fixes.length === 0 ? (
|
|
89
|
+
<View style={s.empty}>
|
|
90
|
+
<Text style={s.emptyText}>
|
|
91
|
+
{session?.active
|
|
92
|
+
? 'Agent is exploring the app...'
|
|
93
|
+
: 'No fixes yet. Start a test session.'}
|
|
94
|
+
</Text>
|
|
95
|
+
</View>
|
|
96
|
+
) : (
|
|
97
|
+
fixes.map((fix, i) => (
|
|
98
|
+
<FixItem
|
|
99
|
+
key={fix.id}
|
|
100
|
+
fix={fix}
|
|
101
|
+
index={i + 1}
|
|
102
|
+
expanded={expanded.has(fix.id)}
|
|
103
|
+
onToggle={() => toggleExpand(fix.id)}
|
|
104
|
+
color={color}
|
|
105
|
+
/>
|
|
106
|
+
))
|
|
107
|
+
)}
|
|
108
|
+
</ScrollView>
|
|
109
|
+
|
|
110
|
+
{/* Summary footer */}
|
|
111
|
+
{fixes.length > 0 && !session?.active && (
|
|
112
|
+
<View style={s.footer}>
|
|
113
|
+
<Text style={s.footerText}>
|
|
114
|
+
{fixes.filter((f) => f.verified).length}/{fixes.length} verified
|
|
115
|
+
{' \u00B7 '}
|
|
116
|
+
Changes are staged, not committed.
|
|
117
|
+
</Text>
|
|
118
|
+
</View>
|
|
119
|
+
)}
|
|
120
|
+
</View>
|
|
121
|
+
</View>
|
|
122
|
+
</Modal>
|
|
123
|
+
);
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
function FixItem({
|
|
127
|
+
fix,
|
|
128
|
+
index,
|
|
129
|
+
expanded,
|
|
130
|
+
onToggle,
|
|
131
|
+
color,
|
|
132
|
+
}: {
|
|
133
|
+
fix: TestFix;
|
|
134
|
+
index: number;
|
|
135
|
+
expanded: boolean;
|
|
136
|
+
onToggle: () => void;
|
|
137
|
+
color: string;
|
|
138
|
+
}) {
|
|
139
|
+
return (
|
|
140
|
+
<Pressable onPress={onToggle} style={s.fixItem}>
|
|
141
|
+
{/* Fix header */}
|
|
142
|
+
<View style={s.fixHeader}>
|
|
143
|
+
<View style={[s.fixIndex, { backgroundColor: fix.verified ? '#22c55e20' : `${color}20` }]}>
|
|
144
|
+
<Text style={[s.fixIndexText, { color: fix.verified ? '#22c55e' : color }]}>
|
|
145
|
+
{fix.verified ? '\u2713' : index}
|
|
146
|
+
</Text>
|
|
147
|
+
</View>
|
|
148
|
+
<View style={{ flex: 1 }}>
|
|
149
|
+
<Text style={s.fixDesc}>{fix.description}</Text>
|
|
150
|
+
<Text style={s.fixFile}>
|
|
151
|
+
{fix.file}{fix.line ? `:${fix.line}` : ''}
|
|
152
|
+
</Text>
|
|
153
|
+
</View>
|
|
154
|
+
<Text style={s.expandIcon}>{expanded ? '\u25B2' : '\u25BC'}</Text>
|
|
155
|
+
</View>
|
|
156
|
+
|
|
157
|
+
{/* Error that triggered fix */}
|
|
158
|
+
{fix.error && (
|
|
159
|
+
<View style={s.fixError}>
|
|
160
|
+
<Text style={s.fixErrorText}>{fix.error}</Text>
|
|
161
|
+
</View>
|
|
162
|
+
)}
|
|
163
|
+
|
|
164
|
+
{/* Expanded: diff/code */}
|
|
165
|
+
{expanded && fix.diff && (
|
|
166
|
+
<View style={s.diffBlock}>
|
|
167
|
+
<Text style={s.diffText}>{fix.diff}</Text>
|
|
168
|
+
</View>
|
|
169
|
+
)}
|
|
170
|
+
</Pressable>
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const s = StyleSheet.create({
|
|
175
|
+
overlay: {
|
|
176
|
+
flex: 1,
|
|
177
|
+
backgroundColor: 'rgba(0,0,0,0.8)',
|
|
178
|
+
justifyContent: 'flex-end',
|
|
179
|
+
},
|
|
180
|
+
container: {
|
|
181
|
+
backgroundColor: '#0a0a0a',
|
|
182
|
+
borderTopLeftRadius: 20,
|
|
183
|
+
borderTopRightRadius: 20,
|
|
184
|
+
maxHeight: '85%',
|
|
185
|
+
borderWidth: 1,
|
|
186
|
+
borderColor: '#1a1a1a',
|
|
187
|
+
borderBottomWidth: 0,
|
|
188
|
+
},
|
|
189
|
+
header: {
|
|
190
|
+
flexDirection: 'row',
|
|
191
|
+
alignItems: 'center',
|
|
192
|
+
padding: 16,
|
|
193
|
+
borderBottomWidth: 1,
|
|
194
|
+
borderBottomColor: '#1a1a1a',
|
|
195
|
+
},
|
|
196
|
+
title: {
|
|
197
|
+
fontSize: 16,
|
|
198
|
+
fontWeight: '700',
|
|
199
|
+
color: '#e5e5e5',
|
|
200
|
+
fontFamily: 'Courier',
|
|
201
|
+
},
|
|
202
|
+
subtitle: {
|
|
203
|
+
fontSize: 11,
|
|
204
|
+
color: '#666',
|
|
205
|
+
marginTop: 2,
|
|
206
|
+
fontFamily: 'Courier',
|
|
207
|
+
},
|
|
208
|
+
closeBtn: { padding: 8 },
|
|
209
|
+
closeBtnText: { color: '#666', fontSize: 16 },
|
|
210
|
+
statusBar: {
|
|
211
|
+
flexDirection: 'row',
|
|
212
|
+
alignItems: 'center',
|
|
213
|
+
paddingHorizontal: 16,
|
|
214
|
+
paddingVertical: 8,
|
|
215
|
+
gap: 8,
|
|
216
|
+
},
|
|
217
|
+
statusDot: {
|
|
218
|
+
width: 6,
|
|
219
|
+
height: 6,
|
|
220
|
+
borderRadius: 3,
|
|
221
|
+
},
|
|
222
|
+
statusText: {
|
|
223
|
+
fontSize: 11,
|
|
224
|
+
fontWeight: '600',
|
|
225
|
+
fontFamily: 'Courier',
|
|
226
|
+
},
|
|
227
|
+
list: { flex: 1 },
|
|
228
|
+
listContent: { padding: 12, gap: 8 },
|
|
229
|
+
empty: {
|
|
230
|
+
padding: 32,
|
|
231
|
+
alignItems: 'center',
|
|
232
|
+
},
|
|
233
|
+
emptyText: {
|
|
234
|
+
color: '#444',
|
|
235
|
+
fontSize: 13,
|
|
236
|
+
fontFamily: 'Courier',
|
|
237
|
+
},
|
|
238
|
+
fixItem: {
|
|
239
|
+
backgroundColor: '#111',
|
|
240
|
+
borderRadius: 10,
|
|
241
|
+
padding: 12,
|
|
242
|
+
borderWidth: 1,
|
|
243
|
+
borderColor: '#1a1a1a',
|
|
244
|
+
},
|
|
245
|
+
fixHeader: {
|
|
246
|
+
flexDirection: 'row',
|
|
247
|
+
alignItems: 'center',
|
|
248
|
+
gap: 10,
|
|
249
|
+
},
|
|
250
|
+
fixIndex: {
|
|
251
|
+
width: 24,
|
|
252
|
+
height: 24,
|
|
253
|
+
borderRadius: 12,
|
|
254
|
+
alignItems: 'center',
|
|
255
|
+
justifyContent: 'center',
|
|
256
|
+
},
|
|
257
|
+
fixIndexText: {
|
|
258
|
+
fontSize: 11,
|
|
259
|
+
fontWeight: '700',
|
|
260
|
+
fontFamily: 'Courier',
|
|
261
|
+
},
|
|
262
|
+
fixDesc: {
|
|
263
|
+
fontSize: 12,
|
|
264
|
+
color: '#e5e5e5',
|
|
265
|
+
fontWeight: '600',
|
|
266
|
+
},
|
|
267
|
+
fixFile: {
|
|
268
|
+
fontSize: 10,
|
|
269
|
+
color: '#666',
|
|
270
|
+
fontFamily: 'Courier',
|
|
271
|
+
marginTop: 2,
|
|
272
|
+
},
|
|
273
|
+
expandIcon: {
|
|
274
|
+
color: '#444',
|
|
275
|
+
fontSize: 10,
|
|
276
|
+
},
|
|
277
|
+
fixError: {
|
|
278
|
+
marginTop: 8,
|
|
279
|
+
backgroundColor: '#f8717110',
|
|
280
|
+
borderRadius: 6,
|
|
281
|
+
padding: 8,
|
|
282
|
+
},
|
|
283
|
+
fixErrorText: {
|
|
284
|
+
fontSize: 10,
|
|
285
|
+
color: '#f87171',
|
|
286
|
+
fontFamily: 'Courier',
|
|
287
|
+
},
|
|
288
|
+
diffBlock: {
|
|
289
|
+
marginTop: 8,
|
|
290
|
+
backgroundColor: '#0d0d0d',
|
|
291
|
+
borderRadius: 6,
|
|
292
|
+
padding: 10,
|
|
293
|
+
borderWidth: 1,
|
|
294
|
+
borderColor: '#1a1a1a',
|
|
295
|
+
},
|
|
296
|
+
diffText: {
|
|
297
|
+
fontSize: 10,
|
|
298
|
+
color: '#22c55e',
|
|
299
|
+
fontFamily: 'Courier',
|
|
300
|
+
lineHeight: 16,
|
|
301
|
+
},
|
|
302
|
+
footer: {
|
|
303
|
+
borderTopWidth: 1,
|
|
304
|
+
borderTopColor: '#1a1a1a',
|
|
305
|
+
padding: 12,
|
|
306
|
+
alignItems: 'center',
|
|
307
|
+
},
|
|
308
|
+
footerText: {
|
|
309
|
+
fontSize: 10,
|
|
310
|
+
color: '#666',
|
|
311
|
+
fontFamily: 'Courier',
|
|
312
|
+
},
|
|
313
|
+
});
|