import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:recipe_flutter/features/admin/data/admin_repository.dart'; import 'package:recipe_flutter/features/admin/domain/admin_ai_categorize_result.dart'; import 'package:recipe_flutter/features/admin/domain/admin_ai_trace.dart'; import 'package:recipe_flutter/features/admin/domain/admin_ai_trace_detail.dart'; import 'package:recipe_flutter/features/admin/domain/admin_category_node.dart'; import 'package:recipe_flutter/features/admin/domain/admin_inventory_item.dart'; import 'package:recipe_flutter/features/admin/domain/admin_pantry_item.dart'; import 'package:recipe_flutter/features/admin/domain/admin_product.dart'; import 'package:recipe_flutter/features/admin/domain/ai_model_info.dart'; import 'package:recipe_flutter/features/admin/domain/pending_product.dart'; import 'package:recipe_flutter/features/admin/domain/receipt_alias.dart'; import 'package:recipe_flutter/features/admin/domain/user_admin.dart'; import 'package:recipe_flutter/features/admin/presentation/admin_aliases_panel.dart'; // Test wrapper that implements AdminRepository with minimal methods class TestAdminRepositoryWrapper implements AdminRepository { final FakeAdminRepository _fakeRepo; TestAdminRepositoryWrapper(this._fakeRepo); @override Future> listReceiptAliases() => _fakeRepo.listReceiptAliases(); @override Future> listGlobalProducts() => _fakeRepo.listGlobalProducts(); @override Future updateReceiptAlias(int id, {String? receiptName, int? productId, bool? isGlobal}) => _fakeRepo.updateReceiptAlias(id, receiptName: receiptName, productId: productId, isGlobal: isGlobal); // Stub implementations for other required methods @override Future> aiCategorizeBulk( {List? productIds}) => throw UnimplementedError(); @override Future bulkSetCategory(List ids, {required int? categoryId}) => throw UnimplementedError(); @override Future createAdminInventory( {int? userId, required int productId, required double quantity, required String unit, String? location, String? brand, String? receiptName, String? suitableFor, String? comment}) => throw UnimplementedError(); @override Future createAdminPantry( {int? userId, required int productId, String? location}) => throw UnimplementedError(); @override Future> createProduct(String name, {int? categoryId}) => throw UnimplementedError(); @override Future createUser( {required String username, required String email, required String password, String role = 'user'}) => throw UnimplementedError(); @override Future deleteUser(int userId) => throw UnimplementedError(); @override Future> listAdminInventory( {int? userId, String? sort}) => throw UnimplementedError(); @override Future> listAdminPantry({int? userId}) => throw UnimplementedError(); @override Future> listAiModels() => throw UnimplementedError(); @override Future getAiTraceById(String traceId) => throw UnimplementedError(); @override Future listAiTraces( {required AdminAiTraceSource source, int limit = 25, String? cursor, String? period, bool onlyErrors = false}) => throw UnimplementedError(); @override Future> listCategoryTree() => throw UnimplementedError(); @override Future> listDeletedProducts() => throw UnimplementedError(); @override Future> listPendingProducts() => throw UnimplementedError(); @override Future> listPrivateProducts() => throw UnimplementedError(); @override Future> listProducts() => throw UnimplementedError(); @override Future> listSelectableProductsForAdmin( {bool forceRefresh = false}) => throw UnimplementedError(); @override Future> listUsers() => throw UnimplementedError(); @override Future mergeAdminInventory( {required int sourceInventoryId, required int targetInventoryId}) => throw UnimplementedError(); @override Future mergeProducts( {required int sourceProductId, required int targetProductId}) => throw UnimplementedError(); @override Future mergeProductsPrivate( {required int sourceProductId, required int targetProductId}) => throw UnimplementedError(); @override Future moveAdminInventoryToPantry(int inventoryId) => throw UnimplementedError(); @override Future moveAdminPantryToInventory( int pantryItemId, Map body) => throw UnimplementedError(); @override Future> previewAdminInventoryMerge( {required int sourceInventoryId, required int targetInventoryId}) => throw UnimplementedError(); @override Future> previewMerge( {required int sourceProductId, required int targetProductId}) => throw UnimplementedError(); @override Future promotePrivateProduct(int productId) => throw UnimplementedError(); @override Future removeAdminInventory(int inventoryId) => throw UnimplementedError(); @override Future removeAdminPantryItem(int pantryItemId) => throw UnimplementedError(); @override Future removeProduct(int productId) => throw UnimplementedError(); @override Future removeReceiptAlias(int id) => throw UnimplementedError(); @override Future> resetPassword(int userId) => throw UnimplementedError(); @override Future restoreProduct(int productId) => throw UnimplementedError(); @override Future setPremium(int userId, {required bool isPremium}) => throw UnimplementedError(); @override Future setProductCategory(int productId, {required int? categoryId}) => throw UnimplementedError(); @override Future setProductStatus(int productId, String status) => throw UnimplementedError(); @override Future setRecipeSharing(int userId, {required bool canShareRecipes}) => throw UnimplementedError(); @override Future setRole(int userId, String newRole) => throw UnimplementedError(); @override Future updateAdminInventory(int inventoryId, {int? productId, double? quantity, String? unit, String? location, String? brand, String? receiptName, String? suitableFor, String? comment}) => throw UnimplementedError(); @override Future updateAdminPantry(int pantryItemId, {int? productId, String? location}) => throw UnimplementedError(); @override Future updateCanonicalName(int productId, String canonicalName) => throw UnimplementedError(); @override Future updateCanonicalNamePrivate( int productId, String canonicalName) => throw UnimplementedError(); @override Future updateEmail(int userId, String email) => throw UnimplementedError(); @override Future upsertReceiptAlias( {required String receiptName, required int productId, bool isGlobal = false}) => throw UnimplementedError(); } // Simple fake that only implements the methods we need class FakeAdminRepository { List _aliases = []; List _products = []; Future> listReceiptAliases() async { return _aliases; } Future> listGlobalProducts() async { return _products; } Future updateReceiptAlias(int id, {String? receiptName, int? productId, bool? isGlobal}) async { // Find and update alias final index = _aliases.indexWhere((a) => a.id == id); if (index >= 0) { _aliases[index] = ReceiptAlias( id: id, receiptName: receiptName ?? _aliases[index].receiptName, productId: productId ?? _aliases[index].productId, ownerId: _aliases[index].ownerId, productName: _products .firstWhere((p) => p.id == (productId ?? _aliases[index].productId)) .displayName, isGlobal: isGlobal ?? _aliases[index].isGlobal, ); } } void setAliases(List aliases) { _aliases = aliases; } void setProducts(List products) { _products = products; } } void main() { group('AdminAliasesPanel - Switch Tests', () { late FakeAdminRepository fakeRepo; late TestAdminRepositoryWrapper wrapper; late List mockAliases; late List mockProducts; setUp(() { fakeRepo = FakeAdminRepository(); wrapper = TestAdminRepositoryWrapper(fakeRepo); mockProducts = [ AdminProduct( id: 1, name: 'Test Product', canonicalName: 'Test Product', categoryPath: 'Test > Category', ), ]; fakeRepo.setProducts(mockProducts); }); testWidgets('Switch should be disabled for global alias', (tester) async { mockAliases = [ ReceiptAlias( id: 1, receiptName: 'Global Alias', productId: 1, ownerId: null, productName: 'Test Product', isGlobal: true, ), ]; fakeRepo.setAliases(mockAliases); await tester.pumpWidget( ProviderScope( overrides: [ adminRepositoryProvider.overrideWithValue(wrapper), ], child: MaterialApp( home: Scaffold( body: AdminAliasesPanel(embedded: true), ), ), ), ); await tester.pumpAndSettle(); await tester.tap(find.byIcon(Icons.edit_outlined).first); await tester.pumpAndSettle(); final switchListTileFinder = find.byType(SwitchListTile); expect(switchListTileFinder, findsOneWidget); final switchTile = tester.widget(switchListTileFinder); expect(switchTile.value, isTrue); expect(switchTile.onChanged, isNull); // Disabled expect(find.text('Aliaset är redan globalt.'), findsOneWidget); }); testWidgets('Switch should be enabled for private alias', (tester) async { mockAliases = [ ReceiptAlias( id: 1, receiptName: 'Private Alias', productId: 1, ownerId: 123, productName: 'Test Product', isGlobal: false, ), ]; fakeRepo.setAliases(mockAliases); await tester.pumpWidget( ProviderScope( overrides: [ adminRepositoryProvider.overrideWithValue(wrapper), ], child: MaterialApp( home: Scaffold( body: AdminAliasesPanel(embedded: true), ), ), ), ); await tester.pumpAndSettle(); await tester.tap(find.byIcon(Icons.edit_outlined).first); await tester.pumpAndSettle(); final switchListTileFinder = find.byType(SwitchListTile); expect(switchListTileFinder, findsOneWidget); final switchTile = tester.widget(switchListTileFinder); expect(switchTile.value, isFalse); expect(switchTile.onChanged, isNotNull); // Enabled expect(find.text('Du kan göra privata alias globala.'), findsOneWidget); }); }); }