srs/test/app_test.dart
Aswin Biju 45fcdcebfb Initial commit
Add SRS app: results, shops, orders and users, wired to srs-api
Flutter web/PWA for the lottery agency, built on Riverpod and go_router,
talking to srs-api over HTTP (HttpApi, token kept in SharedPreferences).

- Results, Shops, Orders and Users tabs (Users for admins only), with
  phone, tablet and desktop layouts and shared widgets and theme tokens
- Users: list with search and Active/Inactive filters; user detail with
  shops, sign-in details, and an Access card
- WhatsApp results switch on each user (sendResults on
  PATCH /api/users/:id). Shows the WhatsApp logo (Simple Icons, CC0,
  drawn with flutter_svg), green only when results will really go out.
  Can't be turned on without a phone number ("Add a phone number first"
  opens the phone sheet), and is paused while the user is inactive. The
  users list marks people who get results with the logo.
- Each Access switch has a one-line description; the explanation
  paragraphs are gone
- Remove demo mode: the app always uses the real AP
  test/support and is used only by tests.
2026-11-04 23:21:50 +05:30

151 lines
5.3 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:srs_app/app.dart';
import 'support/fake_api.dart';
import 'package:srs_app/core/providers.dart';
Future<void> boot(WidgetTester t) async {
t.view.physicalSize = const Size(430 * 2, 900 * 2);
t.view.devicePixelRatio = 2;
addTearDown(t.view.reset);
await t.pumpWidget(
ProviderScope(
overrides: [
apiProvider.overrideWithValue(FakeApi()),
startDateProvider.overrideWithValue(DateTime(2026, 10, 16)),
],
child: const SrsApp(),
),
);
await t.pumpAndSettle();
}
Future<void> signIn(WidgetTester t, String u, String p) async {
await t.enterText(find.byType(TextField).at(0), u);
await t.enterText(find.byType(TextField).at(1), p);
await t.tap(find.text('Sign in').last);
await t.pumpAndSettle(const Duration(milliseconds: 400));
}
void main() {
testWidgets('wrong password shows the error line', (t) async {
await boot(t);
expect(find.text('Welcome back'), findsOneWidget);
expect(
find.text('Forgot your password? Contact your administrator.'),
findsOneWidget,
);
await signIn(t, 'owner', 'bad-password');
expect(find.text('Wrong username or password'), findsOneWidget);
});
testWidgets('admin sees results, four tabs and can open a shop', (t) async {
await boot(t);
await signIn(t, 'owner', 'admin123');
expect(find.text('₹7,700'), findsOneWidget);
expect(find.text('Suvarna Keralam · SK-74'), findsOneWidget);
expect(find.text('Users'), findsOneWidget);
await t.tap(find.text('Shops').last);
await t.pumpAndSettle();
expect(find.text('Add shop'), findsOneWidget);
expect(find.text('180 tickets · 2 orders'), findsOneWidget);
});
testWidgets('normal user has three tabs and no Add shop button', (t) async {
await boot(t);
await signIn(t, 'kzk.staff', 'staff123');
expect(find.text('₹6,300'), findsOneWidget);
expect(find.text('Users'), findsNothing);
await t.tap(find.text('Shops').last);
await t.pumpAndSettle();
expect(find.text('Add shop'), findsNothing);
});
testWidgets('order detail lets a user delete the order after confirming', (
t,
) async {
await boot(t);
await signIn(t, 'kzk.staff', 'staff123');
await t.tap(find.text('Orders').last);
await t.pumpAndSettle();
expect(find.text('Orders'), findsWidgets);
await t.tap(find.textContaining('Photo').first);
await t.pumpAndSettle();
await t.scrollUntilVisible(
find.text('Delete order'),
400,
scrollable: find.byType(Scrollable).first,
);
await t.tap(find.text('Delete order'));
await t.pumpAndSettle();
expect(find.text('Delete this order?'), findsOneWidget);
await t.tap(find.text('Delete order').last);
await t.pumpAndSettle(const Duration(milliseconds: 500));
expect(find.text('Delete this order?'), findsNothing);
});
testWidgets('admin: user toggle and delete both ask for confirmation', (
t,
) async {
await boot(t);
await signIn(t, 'owner', 'admin123');
await t.tap(find.text('Users').last);
await t.pumpAndSettle();
await t.tap(find.text('skm.staff'));
await t.pumpAndSettle();
await t.tap(find.byType(Switch).first);
await t.pumpAndSettle();
expect(find.text('Deactivate skm.staff?'), findsOneWidget);
await t.tap(find.text('Cancel'));
await t.pumpAndSettle();
expect(t.widget<Switch>(find.byType(Switch).first).value, isTrue);
// To the end of the page, clear of the tab bar.
await t.drag(find.byType(Scrollable).first, const Offset(0, -600));
await t.pumpAndSettle();
await t.tap(find.text('Delete user'));
await t.pumpAndSettle();
expect(find.text('Delete skm.staff?'), findsOneWidget);
await t.tap(find.text('Delete user').last);
await t.pumpAndSettle(const Duration(milliseconds: 500));
expect(find.text('skm.staff'), findsNothing);
});
testWidgets('admin: results messages switch, and no phone means no switch', (
t,
) async {
await boot(t);
await signIn(t, 'owner', 'admin123');
await t.tap(find.text('Users').last);
await t.pumpAndSettle();
expect(find.byTooltip('Gets results on WhatsApp'), findsOneWidget);
await t.tap(find.text('kzk.staff'));
await t.pumpAndSettle();
final results = find.byType(Switch).last;
await t.scrollUntilVisible(
find.text('WhatsApp results'),
300,
scrollable: find.byType(Scrollable).first,
);
expect(t.widget<Switch>(results).value, isTrue);
expect(find.text('+91 90000 11111'), findsWidgets);
await t.tap(results);
await t.pumpAndSettle(const Duration(milliseconds: 400));
expect(find.text('WhatsApp results off'), findsOneWidget);
expect(t.widget<Switch>(find.byType(Switch).last).value, isFalse);
await t.tap(find.byTooltip('Back'));
await t.pumpAndSettle(const Duration(milliseconds: 400));
expect(find.byTooltip('Gets results on WhatsApp'), findsNothing);
await t.tap(find.text('skm.staff'));
await t.pumpAndSettle();
await t.scrollUntilVisible(
find.text('Add a phone number first'),
300,
scrollable: find.byType(Scrollable).first,
);
expect(t.widget<Switch>(find.byType(Switch).last).onChanged, isNull);
});
}