import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:motorcycle_dash/core/theme/theme_extensions.dart'; import 'package:motorcycle_dash/features/social/clubs/widgets/slug_input_field.dart'; import 'package:motorcycle_dash/services/clubs/club_providers.dart'; import 'package:motorcycle_dash/services/clubs/club_service.dart'; import 'package:motorcycle_dash/services/clubs/club_slug_validator.dart'; class FakeClubService extends ClubService { final Set takenSlugs; FakeClubService({this.takenSlugs = const {}}); @override Future isSlugAvailable(String slug) async { final validation = ClubSlugValidator.validate(slug); if (!validation.isValid) return false; return !takenSlugs.contains(validation.normalizedSlug!); } } void main() { group('SlugInputField Widget Tests', () { late TextEditingController controller; setUp(() { controller = TextEditingController(); }); tearDown(() { controller.dispose(); }); testWidgets('shows initial guidance and permanence warning', (tester) async { await tester.pumpWidget( ProviderScope( overrides: [ clubServiceProvider.overrideWithValue(FakeClubService()), ], child: MaterialApp( theme: ThemeData.dark().copyWith( extensions: [MotorSignalThemeColors.dark], ), home: Scaffold( body: SlugInputField(controller: controller), ), ), ), ); expect(find.text('Club Handle / Slug *'), findsOneWidget); expect( find.text('3–30 characters: lowercase letters, numbers, and single hyphens.'), findsOneWidget, ); expect( find.text('Club handles are permanent and cannot be changed after creation. Choose carefully.'), findsOneWidget, ); }); testWidgets('shows validation error on invalid or reserved slugs', (tester) async { await tester.pumpWidget( ProviderScope( overrides: [ clubServiceProvider.overrideWithValue(FakeClubService()), ], child: MaterialApp( theme: ThemeData.dark().copyWith( extensions: [MotorSignalThemeColors.dark], ), home: Scaffold( body: SlugInputField(controller: controller), ), ), ), ); // Enter short slug controller.text = 'ab'; await tester.pump(); expect(find.text('Club slug must be at least 3 characters'), findsOneWidget); // Enter reserved slug controller.text = 'admin'; await tester.pump(); expect(find.text('This club slug is reserved'), findsOneWidget); }); testWidgets('debounces and reports availability', (tester) async { bool? lastAvailability; await tester.pumpWidget( ProviderScope( overrides: [ clubServiceProvider.overrideWithValue( FakeClubService(takenSlugs: {'taken-slug'}), ), ], child: MaterialApp( theme: ThemeData.dark().copyWith( extensions: [MotorSignalThemeColors.dark], ), home: Scaffold( body: SlugInputField( controller: controller, onValidationChanged: (val) => lastAvailability = val, ), ), ), ), ); // Enter valid available slug controller.text = 'cool-riders'; await tester.pump(); // immediate update: checking expect(find.text('Checking handle availability...'), findsOneWidget); await tester.pump(const Duration(milliseconds: 500)); // after debounce expect(find.text('Handle is available'), findsOneWidget); expect(lastAvailability, isTrue); // Enter taken slug controller.text = 'taken-slug'; await tester.pump(); await tester.pump(const Duration(milliseconds: 500)); expect(find.text('Handle is already reserved by another club'), findsOneWidget); expect(lastAvailability, isFalse); }); }); }