Files
oott/frontend/test/widget/snackbar_host_test.dart
T
rzuastiandClaude Opus 4.8 587e097291 Add permanent device deletion and refine frontend UI/snackbars
Backend:
- Add db::devices::delete to erase a device and its events atomically
- Expose DELETE /api/devices/{mac}/permanently, wired to OpenAPI
- Cover the new db method and endpoint with tests

Frontend:
- Delete action for not-registered devices (detail screen + list row)
  and an opt-in "permanently delete" checkbox in the Forget dialog
- Navigate to the devices list after deleting from the detail screen
- Refine button emphasis to M3: single filled primary, error-colored
  text buttons for destructive actions, Test demoted to filled-tonal
- Flash the backend-config Test button red on a failed connection test
- Render snackbars through a top-level ScaffoldMessenger host so they
  show above dialogs; keep the built-in SnackBar (with an Overlay host)

Docs:
- CLAUDE.md: rustfmt edition 2024, don't revert formatter-only changes,
  prefer built-in Flutter components, follow existing patterns + M3

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 09:49:11 -04:00

67 lines
2.0 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:frontend/theme/catppuccin_mocha_theme.dart';
import 'package:frontend/utils/ui_snackbars.dart';
import '../helpers/pump_app.dart';
void main() {
// Builds an app wired exactly like main.dart: the router/Navigator sits under
// the snackbar host so snackbars render above dialogs.
Future<BuildContext> pumpHostedApp(WidgetTester tester) async {
late BuildContext pageContext;
await tester.pumpWidget(
MaterialApp(
theme: catppuccinMochaDarkTheme,
builder: buildSnackbarHost,
home: Builder(
builder: (context) {
pageContext = context;
return const Scaffold(body: SizedBox.expand());
},
),
),
);
return pageContext;
}
testWidgets('snackbar shows through the host above an open dialog', (
tester,
) async {
final context = await pumpHostedApp(tester);
// Open a dialog so a modal barrier is on screen.
showDialog<void>(
context: context,
builder: (_) => const AlertDialog(title: Text('A dialog')),
);
await tester.pumpAndSettle();
expect(find.text('A dialog'), findsOneWidget);
UISnackbars.showSuccess(context, 'Hello there');
await tester.pumpAndSettle();
expect(find.byType(SnackBar), findsOneWidget);
expect(find.text('Hello there'), findsOneWidget);
await tearDownTree(tester);
});
testWidgets('the snackbar close-icon tooltip does not crash for lack of an '
'overlay', (tester) async {
final context = await pumpHostedApp(tester);
UISnackbars.showError(context, 'Boom');
await tester.pumpAndSettle();
// Long-pressing the close icon shows its tooltip, which needs an Overlay
// ancestor — the previous host had none and threw "No Overlay widget found".
await tester.longPress(find.byIcon(Icons.close));
await tester.pump(const Duration(seconds: 1));
expect(tester.takeException(), isNull);
await tearDownTree(tester);
});
}