V3-12: crash reporting (code only)

shouldInitializeCrashReporting() and scrubExtra() are pure, fully unit-tested decision/scrubbing functions wired into sentry_flutter via beforeSend/beforeBreadcrumb. Config.crashReportingEnabled defaults off; the DSN is a compile-time --dart-define, not a preference. RecordingEngine gained an injected onUnexpectedStop callback, firing once when restoreAfterProcessDeath finds a trip still 'recording' at launch -- the process died without a user stop.

Marked partially done: the ticket's real acceptance criteria (a forced crash landing in a real Sentry project from a release build, inspecting a real payload) need a Sentry account and a release build this environment can't produce.
This commit is contained in:
2026-08-17 19:32:54 -05:00
parent 0e605ef174
commit be50448917
14 changed files with 443 additions and 9 deletions

View File

@@ -90,10 +90,36 @@ void main() {
expect(config.mountedMode, isTrue);
});
testWidgets('the crash reporting switch writes through to Config (V3-12)',
(tester) async {
await tester.pumpWidget(host());
await tester.pumpAndSettle();
final initial = tester
.widget<SwitchListTile>(find.byKey(const Key('crash-reporting-switch')))
.value;
expect(initial, isFalse, reason: 'off until a privacy policy exists');
await tester.tap(find.byKey(const Key('crash-reporting-switch')));
await tester.pumpAndSettle();
expect(config.crashReportingEnabled, isTrue);
});
/// The Sync section has been pushed below the default test viewport by every section
/// added above it since this suite was first written (V3-05, then V3-12) -- a
/// `ListView` does not mount elements outside its viewport/cache extent, so `find`
/// cannot see them until scrolled into view.
Future<void> scrollToSync(WidgetTester tester) async {
await tester.drag(find.byType(ListView).first, const Offset(0, -600));
await tester.pumpAndSettle();
}
group('upload endpoint', () {
testWidgets('a valid https URL is saved', (tester) async {
await tester.pumpWidget(host());
await tester.pumpAndSettle();
await scrollToSync(tester);
await tester.enterText(
find.byKey(const Key('endpoint-field')),
@@ -110,6 +136,7 @@ void main() {
'silently stored', (tester) async {
await tester.pumpWidget(host());
await tester.pumpAndSettle();
await scrollToSync(tester);
await tester.enterText(
find.byKey(const Key('endpoint-field')),
@@ -126,6 +153,7 @@ void main() {
testWidgets('a non-http scheme is rejected', (tester) async {
await tester.pumpWidget(host());
await tester.pumpAndSettle();
await scrollToSync(tester);
await tester.enterText(
find.byKey(const Key('endpoint-field')),
@@ -142,6 +170,7 @@ void main() {
await config.setUploadEndpoint('https://example.test/ingest');
await tester.pumpWidget(host());
await tester.pumpAndSettle();
await scrollToSync(tester);
await tester.enterText(find.byKey(const Key('endpoint-field')), '');
await tester.tap(find.byKey(const Key('save-endpoint')));
@@ -156,13 +185,11 @@ void main() {
await tester.pumpWidget(host());
await tester.pumpAndSettle();
await scrollToSync(tester);
expect(find.text(config.deviceId), findsOneWidget);
expect(find.byKey(const Key('copy-device-id')), findsOneWidget);
// The list has grown past one screen (V3-05 added a section above this), so the
// control is not necessarily within the default test viewport.
await tester.drag(find.byType(ListView).first, const Offset(0, -400));
await tester.pumpAndSettle();
// Tapping must not throw even though no real clipboard exists in the test harness.
await tester.tap(find.byKey(const Key('copy-device-id')));
await tester.pumpAndSettle();