fix : update local.properties path and improve null safety in chicken_local_imp.dart and chicken_repository_imp.dart; refactor profile view for better readability

This commit is contained in:
2025-10-19 09:47:33 +03:30
parent 464dacc39b
commit 6e4e3159d1
17 changed files with 3183 additions and 362 deletions

View File

@@ -44,12 +44,11 @@ class ChickenLocalDataSourceImp implements ChickenLocalDataSource {
path: ChickenRoutes.buysInProvinceSteward,
),
];
}
@override
WidelyUsedLocalModel? getAllWidely() {
var res = local.readBox<WidelyUsedLocalModel>(boxName: boxName);
return res?.first;
return res?.isNotEmpty == true ? res!.first : null;
}
}

View File

@@ -47,7 +47,9 @@ class ChickenRepositoryImp implements ChickenRepository {
}
@override
Future<KillHouseDistributionInfo?> getKillHouseDistributionInfo({required String token}) async {
Future<KillHouseDistributionInfo?> getKillHouseDistributionInfo({
required String token,
}) async {
var res = await remote.getKillHouseDistributionInfo(token: token);
return res;
}
@@ -57,7 +59,10 @@ class ChickenRepositoryImp implements ChickenRepository {
required String token,
Map<String, dynamic>? queryParameters,
}) async {
var res = await remote.getGeneralBarInformation(token: token, queryParameters: queryParameters);
var res = await remote.getGeneralBarInformation(
token: token,
queryParameters: queryParameters,
);
return res;
}
@@ -66,7 +71,10 @@ class ChickenRepositoryImp implements ChickenRepository {
required String token,
Map<String, dynamic>? queryParameters,
}) async {
var res = await remote.getWaitingArrivals(token: token, queryParameters: queryParameters);
var res = await remote.getWaitingArrivals(
token: token,
queryParameters: queryParameters,
);
return res;
}
@@ -83,7 +91,10 @@ class ChickenRepositoryImp implements ChickenRepository {
required String token,
Map<String, dynamic>? queryParameters,
}) async {
var res = await remote.getImportedLoadsModel(token: token, queryParameters: queryParameters);
var res = await remote.getImportedLoadsModel(
token: token,
queryParameters: queryParameters,
);
return res;
}
@@ -92,7 +103,10 @@ class ChickenRepositoryImp implements ChickenRepository {
required String token,
Map<String, dynamic>? queryParameters,
}) async {
var res = await remote.getAllocatedMade(token: token, queryParameters: queryParameters);
var res = await remote.getAllocatedMade(
token: token,
queryParameters: queryParameters,
);
return res;
}
@@ -105,7 +119,10 @@ class ChickenRepositoryImp implements ChickenRepository {
}
@override
Future<void> denyAllocation({required String token, required String allocationToken}) async {
Future<void> denyAllocation({
required String token,
required String allocationToken,
}) async {
await remote.denyAllocation(token: token, allocationToken: allocationToken);
}
@@ -114,7 +131,10 @@ class ChickenRepositoryImp implements ChickenRepository {
required String token,
required List<String> allocationTokens,
}) async {
await remote.confirmAllAllocation(token: token, allocationTokens: allocationTokens);
await remote.confirmAllAllocation(
token: token,
allocationTokens: allocationTokens,
);
}
@override
@@ -128,7 +148,10 @@ class ChickenRepositoryImp implements ChickenRepository {
required String token,
Map<String, dynamic>? queryParameters,
}) async {
var res = await remote.getGuilds(token: token, queryParameters: queryParameters);
var res = await remote.getGuilds(
token: token,
queryParameters: queryParameters,
);
return res;
}
@@ -151,7 +174,10 @@ class ChickenRepositoryImp implements ChickenRepository {
required String token,
Map<String, dynamic>? queryParameters,
}) async {
await remote.deleteStewardAllocation(token: token, queryParameters: queryParameters);
await remote.deleteStewardAllocation(
token: token,
queryParameters: queryParameters,
);
}
@override
@@ -191,7 +217,8 @@ class ChickenRepositoryImp implements ChickenRepository {
}
@override
Future<PaginationModel<StewardFreeBar>?> getStewardPurchasesOutSideOfTheProvince({
Future<PaginationModel<StewardFreeBar>?>
getStewardPurchasesOutSideOfTheProvince({
required String token,
Map<String, dynamic>? queryParameters,
}) async {
@@ -203,13 +230,17 @@ class ChickenRepositoryImp implements ChickenRepository {
}
@override
Future<List<IranProvinceCityModel>?> getCity({required String provinceName}) async {
Future<List<IranProvinceCityModel>?> getCity({
required String provinceName,
}) async {
var res = await remote.getCity(provinceName: provinceName);
return res;
}
@override
Future<List<IranProvinceCityModel>?> getProvince({CancelToken? cancelToken}) async {
Future<List<IranProvinceCityModel>?> getProvince({
CancelToken? cancelToken,
}) async {
var res = await remote.getProvince(cancelToken: cancelToken);
return res;
}
@@ -219,7 +250,10 @@ class ChickenRepositoryImp implements ChickenRepository {
required String token,
required CreateStewardFreeBar body,
}) async {
await remote.createStewardPurchasesOutSideOfTheProvince(token: token, body: body);
await remote.createStewardPurchasesOutSideOfTheProvince(
token: token,
body: body,
);
}
@override
@@ -229,7 +263,8 @@ class ChickenRepositoryImp implements ChickenRepository {
}) async {
return await remote.editStewardPurchasesOutSideOfTheProvince(
token: token,
queryParameters: body.toJson()..removeWhere((key, value) => value == null),
queryParameters: body.toJson()
..removeWhere((key, value) => value == null),
);
}
@@ -245,7 +280,8 @@ class ChickenRepositoryImp implements ChickenRepository {
}
@override
Future<PaginationModel<OutProvinceCarcassesBuyer>?> getOutProvinceCarcassesBuyer({
Future<PaginationModel<OutProvinceCarcassesBuyer>?>
getOutProvinceCarcassesBuyer({
required String token,
Map<String, dynamic>? queryParameters,
}) async {
@@ -269,7 +305,10 @@ class ChickenRepositoryImp implements ChickenRepository {
required String token,
Map<String, dynamic>? queryParameters,
}) async {
var res = await remote.getStewardFreeSaleBar(token: token, queryParameters: queryParameters);
var res = await remote.getStewardFreeSaleBar(
token: token,
queryParameters: queryParameters,
);
return res;
}
@@ -290,7 +329,10 @@ class ChickenRepositoryImp implements ChickenRepository {
}
@override
Future<void> deleteOutProvinceStewardFreeBar({required String token, required String key}) async {
Future<void> deleteOutProvinceStewardFreeBar({
required String token,
required String key,
}) async {
await remote.deleteOutProvinceStewardFreeBar(token: token, key: key);
}
@@ -301,7 +343,10 @@ class ChickenRepositoryImp implements ChickenRepository {
}
@override
Future<void> updateUserProfile({required String token, required UserProfile userProfile}) async {
Future<void> updateUserProfile({
required String token,
required UserProfile userProfile,
}) async {
await remote.updateUserProfile(token: token, userProfile: userProfile);
}
@@ -318,17 +363,26 @@ class ChickenRepositoryImp implements ChickenRepository {
required String token,
Map<String, dynamic>? queryParameters,
}) async {
var res = await remote.getSegmentation(token: token, queryParameters: queryParameters);
var res = await remote.getSegmentation(
token: token,
queryParameters: queryParameters,
);
return res;
}
@override
Future<void> createSegmentation({required String token, required SegmentationModel model}) async {
Future<void> createSegmentation({
required String token,
required SegmentationModel model,
}) async {
await remote.createSegmentation(token: token, model: model);
}
@override
Future<void> editSegmentation({required String token, required SegmentationModel model}) async {
Future<void> editSegmentation({
required String token,
required SegmentationModel model,
}) async {
await remote.editSegmentation(token: token, model: model);
}
@@ -354,7 +408,9 @@ class ChickenRepositoryImp implements ChickenRepository {
WidelyUsedLocalModel? getAllWidely() => local.getAllWidely();
@override
Future<void> initWidleyUsed() async {}
Future<void> initWidleyUsed() async {
await local.initWidleyUsed();
}
@override
Future<StewardSalesInfoDashboard?> getStewardSalesInfoDashboard({

View File

@@ -32,7 +32,11 @@ class ProfilePage extends GetView<ProfileLogic> {
return Container(
width: 128.w,
height: 128.h,
child: Center(child: CupertinoActivityIndicator(color: AppColor.greenNormal)),
child: Center(
child: CupertinoActivityIndicator(
color: AppColor.greenNormal,
),
),
);
}
@@ -71,7 +75,7 @@ class ProfilePage extends GetView<ProfileLogic> {
children: [
rolesWidget(),
SizedBox(height: 12.h,),
SizedBox(height: 12.h),
ObxValue((data) {
if (data.value.status == ResourceStatus.loading) {
@@ -96,7 +100,7 @@ class ProfilePage extends GetView<ProfileLogic> {
Visibility(
visible:
data.value.data?.unitName != null ||
data.value.data?.unitName != null ||
data.value.data?.unitAddress != null ||
data.value.data?.unitPostalCode != null ||
data.value.data?.unitRegistrationNumber != null ||
@@ -124,71 +128,93 @@ class ProfilePage extends GetView<ProfileLogic> {
}
}, controller.userProfile),
GestureDetector(
onTap: () {
Get.bottomSheet(
changePasswordBottomSheet(),
isScrollControlled: true,
);
},
child: Container(
height: 47.h,
margin: EdgeInsets.symmetric(horizontal: 8, vertical: 8.h),
padding: EdgeInsets.symmetric(horizontal: 11.h, vertical: 8.h),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
border: Border.all(width: 1, color: const Color(0xFFD6D6D6)),
onTap: () {
Get.bottomSheet(
changePasswordBottomSheet(),
isScrollControlled: true,
);
},
child: Container(
height: 47.h,
margin: EdgeInsets.symmetric(horizontal: 8, vertical: 8.h),
padding: EdgeInsets.symmetric(
horizontal: 11.h,
vertical: 8.h,
),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
border: Border.all(
width: 1,
color: const Color(0xFFD6D6D6),
),
child: Row(
spacing: 6,
children: [
Assets.vec.lockSvg.svg(
width: 24.w,
height: 24.h,
colorFilter: ColorFilter.mode(AppColor.blueNormal, BlendMode.srcIn),
),
child: Row(
spacing: 6,
children: [
Assets.vec.lockSvg.svg(
width: 24.w,
height: 24.h,
colorFilter: ColorFilter.mode(
AppColor.blueNormal,
BlendMode.srcIn,
),
Text(
'تغییر رمز عبور',
textAlign: TextAlign.center,
style: AppFonts.yekan14.copyWith(color: AppColor.blueNormal),
),
Text(
'تغییر رمز عبور',
textAlign: TextAlign.center,
style: AppFonts.yekan14.copyWith(
color: AppColor.blueNormal,
),
],
),
)),
),
],
),
),
),
GestureDetector(
onTap: () {
Get.bottomSheet(
changePasswordBottomSheet(),
isScrollControlled: true,
);
},
child: Container(
height: 47.h,
margin: EdgeInsets.symmetric(horizontal: 8),
padding: EdgeInsets.symmetric(horizontal: 11.h, vertical: 8.h),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
border: Border.all(width: 1, color: const Color(0xFFD6D6D6)),
onTap: () {
Get.bottomSheet(
changePasswordBottomSheet(),
isScrollControlled: true,
);
},
child: Container(
height: 47.h,
margin: EdgeInsets.symmetric(horizontal: 8),
padding: EdgeInsets.symmetric(
horizontal: 11.h,
vertical: 8.h,
),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
border: Border.all(
width: 1,
color: const Color(0xFFD6D6D6),
),
child: Row(
spacing: 6,
children: [
Assets.vec.logoutSvg.svg(
width: 24.w,
height: 24.h,
colorFilter: ColorFilter.mode(AppColor.redNormal, BlendMode.srcIn),
),
child: Row(
spacing: 6,
children: [
Assets.vec.logoutSvg.svg(
width: 24.w,
height: 24.h,
colorFilter: ColorFilter.mode(
AppColor.redNormal,
BlendMode.srcIn,
),
Text(
'خروج',
textAlign: TextAlign.center,
style: AppFonts.yekan14.copyWith(color: AppColor.redNormal),
),
Text(
'خروج',
textAlign: TextAlign.center,
style: AppFonts.yekan14.copyWith(
color: AppColor.redNormal,
),
],
),
)),
),
],
),
),
),
SizedBox(height: 100),
],
@@ -200,17 +226,16 @@ class ProfilePage extends GetView<ProfileLogic> {
Container invoiceIssuanceInformation() => Container();
Widget bankInformationWidget() =>
Column(
spacing: 16,
children: [
itemList(title: 'نام بانک', content: 'سامان'),
itemList(title: 'نام صاحب حساب', content: 'رضا رضایی'),
itemList(title: 'شماره کارت ', content: '54154545415'),
itemList(title: 'شماره حساب', content: '62565263263652'),
itemList(title: 'شماره شبا', content: '62565263263652'),
],
);
Widget bankInformationWidget() => Column(
spacing: 16,
children: [
itemList(title: 'نام بانک', content: 'سامان'),
itemList(title: 'نام صاحب حساب', content: 'رضا رضایی'),
itemList(title: 'شماره کارت ', content: '54154545415'),
itemList(title: 'شماره حساب', content: '62565263263652'),
itemList(title: 'شماره شبا', content: '62565263263652'),
],
);
Widget userProfileInformation(Resource<UserProfile> value) {
UserProfile item = value.data!;
@@ -219,116 +244,131 @@ class ProfilePage extends GetView<ProfileLogic> {
children: [
Positioned.fill(
child: ObxValue(
(val) =>
Container(
height: val.value ? 320.h : 47.h,
margin: EdgeInsets.symmetric(horizontal: 8, vertical: val.value ? 8 : 0),
padding: EdgeInsets.symmetric(horizontal: 11.h, vertical: 8.h),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
border: Border.all(width: 0.5, color: AppColor.darkGreyLight),
),
child: val.value
? Column(
spacing: 6,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
GestureDetector(
onTap: () {
Get.bottomSheet(
userInformationBottomSheet(),
isScrollControlled: true,
ignoreSafeArea: false,
);
},
child: Assets.vec.editSvg.svg(
width: 24.w,
height: 24.h,
colorFilter: ColorFilter.mode(AppColor.blueNormal, BlendMode.srcIn),
),
),
],
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0),
child: Column(
(val) => Container(
height: val.value ? 320.h : 47.h,
margin: EdgeInsets.symmetric(
horizontal: 8,
vertical: val.value ? 8 : 0,
),
padding: EdgeInsets.symmetric(horizontal: 11.h, vertical: 8.h),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
border: Border.all(width: 0.5, color: AppColor.darkGreyLight),
),
child: val.value
? Column(
spacing: 6,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
itemList(
title: 'نام و نام خانوادگی',
content: item.fullname ?? 'نامشخص',
icon: Assets.vec.userSvg.path,
hasColoredBox: true,
),
itemList(
title: 'کدملی',
content: item.nationalId ?? 'نامشخص',
icon: Assets.vec.tagUserSvg.path,
),
itemList(
title: 'موبایل',
content: item.mobile ?? 'نامشخص',
icon: Assets.vec.callSvg.path,
),
itemList(
title: 'شماره شناسنامه',
content: item.nationalCode ?? 'نامشخص',
icon: Assets.vec.userSquareSvg.path,
),
itemList(
title: 'تاریخ تولد',
content: item.birthday?.toJalali.formatCompactDate() ?? 'نامشخص',
icon: Assets.vec.calendarSvg.path,
),
//todo
itemList(
title: 'استان',
content: item.province ?? 'نامشخص',
icon: Assets.vec.pictureFrameSvg.path,
),
itemList(
title: 'شهر',
content: item.city ?? 'نامشخص',
icon: Assets.vec.mapSvg.path,
GestureDetector(
onTap: () {
Get.bottomSheet(
userInformationBottomSheet(),
isScrollControlled: true,
ignoreSafeArea: false,
);
},
child: Assets.vec.editSvg.svg(
width: 24.w,
height: 24.h,
colorFilter: ColorFilter.mode(
AppColor.blueNormal,
BlendMode.srcIn,
),
),
),
],
),
),
],
)
: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [Icon(CupertinoIcons.chevron_down, color: AppColor.iconColor)],
),
),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 12.0,
vertical: 8.0,
),
child: Column(
children: [
itemList(
title: 'نام و نام خانوادگی',
content: item.fullname ?? 'نامشخص',
icon: Assets.vec.userSvg.path,
hasColoredBox: true,
),
itemList(
title: 'کدملی',
content: item.nationalId ?? 'نامشخص',
icon: Assets.vec.tagUserSvg.path,
),
itemList(
title: 'موبایل',
content: item.mobile ?? 'نامشخص',
icon: Assets.vec.callSvg.path,
),
itemList(
title: 'شماره شناسنامه',
content: item.nationalCode ?? 'نامشخص',
icon: Assets.vec.userSquareSvg.path,
),
itemList(
title: 'تاریخ تولد',
content:
item.birthday?.toJalali
.formatCompactDate() ??
'نامشخص',
icon: Assets.vec.calendarSvg.path,
),
//todo
itemList(
title: 'استان',
content: item.province ?? 'نامشخص',
icon: Assets.vec.pictureFrameSvg.path,
),
itemList(
title: 'شهر',
content: item.city ?? 'نامشخص',
icon: Assets.vec.mapSvg.path,
),
],
),
),
],
)
: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Icon(
CupertinoIcons.chevron_down,
color: AppColor.iconColor,
),
],
),
),
controller.isUserInformationOpen,
),
),
ObxValue(
(isOpen) =>
AnimatedPositioned(
right: 16,
top: isOpen.value ? -7 : 11,
duration: Duration(milliseconds: 500),
child: Container(
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: isOpen.value
? BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
border: Border.all(width: 0.5, color: Color(0xFFA9A9A9)),
)
: null,
child: Text(
'اطلاعات هویتی',
style: AppFonts.yekan16.copyWith(color: AppColor.iconColor),
),
),
(isOpen) => AnimatedPositioned(
right: 16,
top: isOpen.value ? -7 : 11,
duration: Duration(milliseconds: 500),
child: Container(
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: isOpen.value
? BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
border: Border.all(width: 0.5, color: Color(0xFFA9A9A9)),
)
: null,
child: Text(
'اطلاعات هویتی',
style: AppFonts.yekan16.copyWith(color: AppColor.iconColor),
),
),
),
controller.isUserInformationOpen,
),
],
@@ -342,118 +382,134 @@ class ProfilePage extends GetView<ProfileLogic> {
children: [
Positioned.fill(
child: ObxValue(
(val) =>
Container(
height: val.value ? 320.h : 47.h,
margin: EdgeInsets.symmetric(horizontal: 8, vertical: val.value ? 12 : 0),
padding: EdgeInsets.symmetric(horizontal: 11.h),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
border: Border.all(width: 0.5, color: AppColor.darkGreyLight),
),
child: val.value
? Column(
children: [
SizedBox(height: 5.h),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
GestureDetector(
onTap: () {
Get.bottomSheet(
userInformationBottomSheet(),
isScrollControlled: true,
ignoreSafeArea: false,
);
},
child: Assets.vec.editSvg.svg(
width: 24.w,
height: 24.h,
colorFilter: ColorFilter.mode(AppColor.blueNormal, BlendMode.srcIn),
),
),
],
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0),
child: Column(
spacing: 2,
(val) => Container(
height: val.value ? 320.h : 47.h,
margin: EdgeInsets.symmetric(
horizontal: 8,
vertical: val.value ? 12 : 0,
),
padding: EdgeInsets.symmetric(horizontal: 11.h),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
border: Border.all(width: 0.5, color: AppColor.darkGreyLight),
),
child: val.value
? Column(
children: [
SizedBox(height: 5.h),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
itemList(
title: 'نام صنفی',
content: item.unitName ?? 'نامشخص',
hasColoredBox: true,
visible: item.unitName != null,
GestureDetector(
onTap: () {
Get.bottomSheet(
userInformationBottomSheet(),
isScrollControlled: true,
ignoreSafeArea: false,
);
},
child: Assets.vec.editSvg.svg(
width: 24.w,
height: 24.h,
colorFilter: ColorFilter.mode(
AppColor.blueNormal,
BlendMode.srcIn,
),
),
),
itemList(
title: 'شناسنامه ملی',
content: item.unitNationalId ?? 'نامشخص',
visible: item.unitName != null,
),
itemList(
title: 'شماره ثبت',
content: item.unitRegistrationNumber ?? 'نامشخص',
visible: item.unitName != null,
),
itemList(
title: 'کد اقتصادی',
content: item.unitEconomicalNumber ?? 'نامشخص',
visible: item.unitName != null,
),
itemList(
title: 'کد پستی',
content: item.unitPostalCode ?? 'نامشخص',
visible: item.unitName != null,
),
itemList(
title: 'استان',
content: item.province ?? 'نامشخص',
visible: item.unitName != null,
),
itemList(
title: 'شهر',
content: item.city ?? 'نامشخص',
visible: item.unitName != null,
),
itemList(title: 'آدرس', content: item.unitAddress ?? 'نامشخص'),
],
),
),
],
)
: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [Icon(CupertinoIcons.chevron_down, color: AppColor.iconColor)],
),
),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 12.0,
vertical: 8.0,
),
child: Column(
spacing: 2,
children: [
itemList(
title: 'نام صنفی',
content: item.unitName ?? 'نامشخص',
hasColoredBox: true,
visible: item.unitName != null,
),
itemList(
title: 'شناسنامه ملی',
content: item.unitNationalId ?? 'نامشخص',
visible: item.unitName != null,
),
itemList(
title: 'شماره ثبت',
content:
item.unitRegistrationNumber ?? 'نامشخص',
visible: item.unitName != null,
),
itemList(
title: 'کد اقتصادی',
content: item.unitEconomicalNumber ?? 'نامشخص',
visible: item.unitName != null,
),
itemList(
title: 'کد پستی',
content: item.unitPostalCode ?? 'نامشخص',
visible: item.unitName != null,
),
itemList(
title: 'استان',
content: item.province ?? 'نامشخص',
visible: item.unitName != null,
),
itemList(
title: 'شهر',
content: item.city ?? 'نامشخص',
visible: item.unitName != null,
),
itemList(
title: 'آدرس',
content: item.unitAddress ?? 'نامشخص',
),
],
),
),
],
)
: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Icon(
CupertinoIcons.chevron_down,
color: AppColor.iconColor,
),
],
),
),
controller.isUnitInformationOpen,
),
),
ObxValue(
(isOpen) =>
AnimatedPositioned(
right: 16,
top: isOpen.value ? -2 : 11,
duration: Duration(milliseconds: 500),
child: Container(
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: isOpen.value
? BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
border: Border.all(width: 0.5, color: Color(0xFFA9A9A9)),
)
: null,
child: Text(
'اطلاعات صنفی',
style: AppFonts.yekan16.copyWith(color: AppColor.iconColor),
),
),
(isOpen) => AnimatedPositioned(
right: 16,
top: isOpen.value ? -2 : 11,
duration: Duration(milliseconds: 500),
child: Container(
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: isOpen.value
? BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
border: Border.all(width: 0.5, color: Color(0xFFA9A9A9)),
)
: null,
child: Text(
'اطلاعات صنفی',
style: AppFonts.yekan16.copyWith(color: AppColor.iconColor),
),
),
),
controller.isUnitInformationOpen,
),
],
@@ -466,37 +522,45 @@ class ProfilePage extends GetView<ProfileLogic> {
String? icon,
bool hasColoredBox = false,
bool? visible,
}) =>
Visibility(
visible: visible ?? true,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 12.h, vertical: 6.h),
decoration: BoxDecoration(
color: hasColoredBox ? AppColor.greenLight : Colors.transparent,
borderRadius: BorderRadius.circular(8),
border: hasColoredBox
? Border.all(width: 0.25, color: AppColor.bgDark)
: Border.all(width: 0, color: Colors.transparent),
),
child: Row(
spacing: 4,
children: [
if (icon != null)
Padding(
padding: const EdgeInsets.only(left: 8.0),
child: SvgGenImage.vec(icon).svg(
width: 20.w,
height: 20.h,
colorFilter: ColorFilter.mode(AppColor.textColor, BlendMode.srcIn),
),
}) => Visibility(
visible: visible ?? true,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 12.h, vertical: 6.h),
decoration: BoxDecoration(
color: hasColoredBox ? AppColor.greenLight : Colors.transparent,
borderRadius: BorderRadius.circular(8),
border: hasColoredBox
? Border.all(width: 0.25, color: AppColor.bgDark)
: Border.all(width: 0, color: Colors.transparent),
),
child: Row(
spacing: 4,
children: [
if (icon != null)
Padding(
padding: const EdgeInsets.only(left: 8.0),
child: SvgGenImage.vec(icon).svg(
width: 20.w,
height: 20.h,
colorFilter: ColorFilter.mode(
AppColor.textColor,
BlendMode.srcIn,
),
Text(title, style: AppFonts.yekan14.copyWith(color: AppColor.textColor)),
Spacer(),
Text(content, style: AppFonts.yekan14.copyWith(color: AppColor.textColor)),
],
),
),
Text(
title,
style: AppFonts.yekan14.copyWith(color: AppColor.textColor),
),
),
);
Spacer(),
Text(
content,
style: AppFonts.yekan14.copyWith(color: AppColor.textColor),
),
],
),
),
);
Widget cardActionWidget({
required String title,
@@ -519,7 +583,9 @@ class ProfilePage extends GetView<ProfileLogic> {
padding: EdgeInsets.all(6),
decoration: ShapeDecoration(
color: cardColor ?? AppColor.blueLight,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: Container(
padding: EdgeInsets.all(4),
@@ -530,7 +596,8 @@ class ProfilePage extends GetView<ProfileLogic> {
child: SvgGenImage.vec(icon).svg(
width: 40.w,
height: 40.h,
colorFilter: color ?? ColorFilter.mode(Colors.white, BlendMode.srcIn),
colorFilter:
color ?? ColorFilter.mode(Colors.white, BlendMode.srcIn),
),
),
),
@@ -554,7 +621,9 @@ class ProfilePage extends GetView<ProfileLogic> {
children: [
Text(
'ویرایش اطلاعات هویتی',
style: AppFonts.yekan16Bold.copyWith(color: AppColor.darkGreyDarkHover),
style: AppFonts.yekan16Bold.copyWith(
color: AppColor.darkGreyDarkHover,
),
),
Container(
@@ -626,7 +695,9 @@ class ProfilePage extends GetView<ProfileLogic> {
children: [
Text(
'عکس پروفایل',
style: AppFonts.yekan16Bold.copyWith(color: AppColor.blueNormal),
style: AppFonts.yekan16Bold.copyWith(
color: AppColor.blueNormal,
),
),
ObxValue((data) {
return Container(
@@ -635,17 +706,29 @@ class ProfilePage extends GetView<ProfileLogic> {
decoration: BoxDecoration(
color: AppColor.lightGreyNormal,
borderRadius: BorderRadius.circular(8),
border: Border.all(width: 1, color: AppColor.blackLight),
border: Border.all(
width: 1,
color: AppColor.blackLight,
),
),
child: Center(
child: data.value == null
? Padding(
padding: const EdgeInsets.fromLTRB(30, 10, 10, 30),
child: Image.network(
controller.userProfile.value.data?.image ?? '',
),
)
: Image.file(File(data.value!.path), fit: BoxFit.cover),
padding: const EdgeInsets.fromLTRB(
30,
10,
10,
30,
),
child: Image.network(
controller.userProfile.value.data?.image ??
'',
),
)
: Image.file(
File(data.value!.path),
fit: BoxFit.cover,
),
),
);
}, controller.selectedImage),
@@ -658,14 +741,18 @@ class ProfilePage extends GetView<ProfileLogic> {
text: 'گالری',
width: 150.w,
height: 40.h,
textStyle: AppFonts.yekan20.copyWith(color: Colors.white),
textStyle: AppFonts.yekan20.copyWith(
color: Colors.white,
),
onPressed: () async {
controller.selectedImage.value = await controller.imagePicker.pickImage(
source: ImageSource.gallery,
imageQuality: 60,
maxWidth: 1080,
maxHeight: 720,
);
controller.selectedImage.value = await controller
.imagePicker
.pickImage(
source: ImageSource.gallery,
imageQuality: 60,
maxWidth: 1080,
maxHeight: 720,
);
},
),
SizedBox(width: 16),
@@ -673,14 +760,18 @@ class ProfilePage extends GetView<ProfileLogic> {
text: 'دوربین',
width: 150.w,
height: 40.h,
textStyle: AppFonts.yekan20.copyWith(color: AppColor.blueNormal),
textStyle: AppFonts.yekan20.copyWith(
color: AppColor.blueNormal,
),
onPressed: () async {
controller.selectedImage.value = await controller.imagePicker.pickImage(
source: ImageSource.camera,
imageQuality: 60,
maxWidth: 1080,
maxHeight: 720,
);
controller.selectedImage.value = await controller
.imagePicker
.pickImage(
source: ImageSource.camera,
imageQuality: 60,
maxWidth: 1080,
maxHeight: 720,
);
},
),
],
@@ -731,7 +822,9 @@ class ProfilePage extends GetView<ProfileLogic> {
children: [
Text(
'تغییر رمز عبور',
style: AppFonts.yekan16Bold.copyWith(color: AppColor.darkGreyDarkHover),
style: AppFonts.yekan16Bold.copyWith(
color: AppColor.darkGreyDarkHover,
),
),
SizedBox(),
RTextField(
@@ -743,7 +836,8 @@ class ProfilePage extends GetView<ProfileLogic> {
validator: (value) {
if (value == null || value.isEmpty) {
return 'رمز عبور را وارد کنید';
} else if (controller.userProfile.value.data?.password != value) {
} else if (controller.userProfile.value.data?.password !=
value) {
return 'رمز عبور صحیح نیست';
}
return null;
@@ -827,7 +921,10 @@ class ProfilePage extends GetView<ProfileLogic> {
child: Column(
spacing: 8,
children: [
Text('خروج', style: AppFonts.yekan16Bold.copyWith(color: AppColor.error)),
Text(
'خروج',
style: AppFonts.yekan16Bold.copyWith(color: AppColor.error),
),
SizedBox(),
Text(
'آیا مطمئن هستید که می‌خواهید از حساب کاربری خود خارج شوید؟',
@@ -847,7 +944,9 @@ class ProfilePage extends GetView<ProfileLogic> {
backgroundColor: AppColor.error,
onPressed: () async {
await Future.wait([
controller.tokenService.deleteModuleTokens(Module.chicken),
controller.tokenService.deleteModuleTokens(
Module.chicken,
),
controller.gService.clearSelectedModule(),
]).then((value) async {
await removeChickenDI();
@@ -890,7 +989,9 @@ class ProfilePage extends GetView<ProfileLogic> {
children: List.generate(item?.length ?? 0, (index) {
Map tmpRole = getFaUserRoleWithOnTap(item?[index]);
return CustomChip(
isSelected: controller.gService.getRoute(Module.chicken) == tmpRole.values.first,
isSelected:
controller.gService.getRoute(Module.chicken) ==
tmpRole.values.first,
title: tmpRole.keys.first,
index: index,
onTap: (int p1) {

View File

@@ -0,0 +1,130 @@
# Chicken Package Test Suite
This directory contains comprehensive unit tests and integration tests for the chicken package.
## Test Structure
### Unit Tests
#### Authentication Tests
- `data/repositories/auth/auth_repository_imp_test.dart` - Tests for AuthRepository implementation
- `data/data_source/remote/auth/auth_remote_imp_test.dart` - Tests for AuthRemoteDataSource implementation
#### Repository Tests
- `data/repositories/chicken/chicken_repository_imp_test.dart` - Tests for ChickenRepository implementation
#### Local Data Source Tests
- `data/data_source/local/chicken_local_imp_test.dart` - Tests for local data source implementation
#### Model Tests
- `data/models/response/user_profile_model/user_profile_model_test.dart` - Tests for UserProfileModel
#### Utility Tests
- `presentation/utils/string_utils_test.dart` - Tests for string utility functions
### Integration Tests
#### Authentication Flow
- `integration/auth_flow_integration_test.dart` - Complete authentication workflow tests
#### Steward Workflow
- `integration/steward_workflow_integration_test.dart` - Steward business logic integration tests
#### Poultry Science
- `integration/poultry_science_integration_test.dart` - Poultry science module integration tests
## Running Tests
### Run All Tests
```bash
flutter test
```
### Run Specific Test Categories
```bash
# Run unit tests only
flutter test test/data/
# Run integration tests only
flutter test test/integration/
# Run specific test file
flutter test test/data/repositories/auth/auth_repository_imp_test.dart
```
### Run Tests with Coverage
```bash
flutter test --coverage
```
## Test Coverage
The test suite covers:
1. **Authentication Components**
- Login/logout functionality
- User info management
- Token handling
- Authentication state management
2. **Repository Layer**
- Data source interactions
- Error handling
- Data transformation
- Caching mechanisms
3. **Local Storage**
- Data persistence
- Cache management
- Offline functionality
4. **Business Logic**
- Steward workflows
- Poultry science operations
- Data validation
- Business rule enforcement
5. **Integration Flows**
- End-to-end user journeys
- Cross-module interactions
- Error propagation
- Performance scenarios
## Test Data
Tests use mock data and mock objects to ensure:
- Fast execution
- Deterministic results
- Isolation from external dependencies
- Easy maintenance
## Mocking Strategy
- **Mocktail** is used for creating mock objects
- **Mock data** is used for consistent test scenarios
- **Fake implementations** are used for complex dependencies
## Best Practices
1. **Test Naming**: Use descriptive test names that explain the scenario
2. **Arrange-Act-Assert**: Follow the AAA pattern for test structure
3. **Single Responsibility**: Each test should verify one specific behavior
4. **Independent Tests**: Tests should not depend on each other
5. **Clean Setup**: Use setUp/tearDown methods for test preparation
## Continuous Integration
Tests are designed to run in CI/CD pipelines with:
- Fast execution times
- No external dependencies
- Deterministic results
- Clear failure reporting
## Maintenance
When adding new features:
1. Write unit tests for new components
2. Add integration tests for new workflows
3. Update existing tests if interfaces change
4. Ensure test coverage remains high
5. Document any new test patterns

View File

@@ -0,0 +1,161 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:rasadyar_chicken/data/data_source/local/chicken_local_imp.dart';
import 'package:rasadyar_chicken/data/models/local/widely_used_local_model.dart';
import 'package:rasadyar_core/core.dart';
class MockHiveLocalStorage extends Mock implements HiveLocalStorage {}
void main() {
late ChickenLocalDataSourceImp chickenLocalDataSource;
late MockHiveLocalStorage mockHiveLocalStorage;
setUp(() {
mockHiveLocalStorage = MockHiveLocalStorage();
// Register the mock in GetIt for dependency injection
if (diCore.isRegistered<HiveLocalStorage>()) {
diCore.unregister<HiveLocalStorage>();
}
diCore.registerSingleton<HiveLocalStorage>(mockHiveLocalStorage);
chickenLocalDataSource = ChickenLocalDataSourceImp();
});
tearDown(() {
// Clean up GetIt registration
if (diCore.isRegistered<HiveLocalStorage>()) {
diCore.unregister<HiveLocalStorage>();
}
});
group('ChickenLocalDataSourceImp', () {
group('openBox', () {
test('should call local openBox with correct box name', () async {
// Arrange
when(
() => mockHiveLocalStorage.openBox<WidelyUsedLocalModel>(
'Chicken_Widley_Box',
),
).thenAnswer((_) async {});
// Act
await chickenLocalDataSource.openBox();
// Assert
verify(
() => mockHiveLocalStorage.openBox<WidelyUsedLocalModel>(
'Chicken_Widley_Box',
),
).called(1);
});
});
group('initWidleyUsed', () {
test('should initialize widely used items', () async {
// Act
await chickenLocalDataSource.initWidleyUsed();
// Assert
// This method currently doesn't interact with the mock, but we can verify it completes
expect(chickenLocalDataSource.initWidleyUsed, isA<Function>());
});
});
group('getAllWidely', () {
test('should return first widely used model when data exists', () async {
// Arrange
final expectedModel = WidelyUsedLocalModel(hasInit: true, items: []);
final mockData = [expectedModel];
when(
() => mockHiveLocalStorage.readBox<WidelyUsedLocalModel>(
boxName: 'Chicken_Widley_Box',
),
).thenReturn(mockData);
// Act
final result = chickenLocalDataSource.getAllWidely();
// Assert
expect(result, equals(expectedModel));
verify(
() => mockHiveLocalStorage.readBox<WidelyUsedLocalModel>(
boxName: 'Chicken_Widley_Box',
),
).called(1);
});
test('should return null when no data exists', () async {
// Arrange
when(
() => mockHiveLocalStorage.readBox<WidelyUsedLocalModel>(
boxName: 'Chicken_Widley_Box',
),
).thenReturn(null);
// Act
final result = chickenLocalDataSource.getAllWidely();
// Assert
expect(result, isNull);
verify(
() => mockHiveLocalStorage.readBox<WidelyUsedLocalModel>(
boxName: 'Chicken_Widley_Box',
),
).called(1);
});
test('should return null when empty list is returned', () async {
// Arrange
when(
() => mockHiveLocalStorage.readBox<WidelyUsedLocalModel>(
boxName: 'Chicken_Widley_Box',
),
).thenReturn([]);
// Act
final result = chickenLocalDataSource.getAllWidely();
// Assert
expect(result, isNull);
verify(
() => mockHiveLocalStorage.readBox<WidelyUsedLocalModel>(
boxName: 'Chicken_Widley_Box',
),
).called(1);
});
test('should return first item when multiple items exist', () async {
// Arrange
final firstModel = WidelyUsedLocalModel(hasInit: true, items: []);
final secondModel = WidelyUsedLocalModel(hasInit: false, items: []);
final mockData = [firstModel, secondModel];
when(
() => mockHiveLocalStorage.readBox<WidelyUsedLocalModel>(
boxName: 'Chicken_Widley_Box',
),
).thenReturn(mockData);
// Act
final result = chickenLocalDataSource.getAllWidely();
// Assert
expect(result, equals(firstModel));
verify(
() => mockHiveLocalStorage.readBox<WidelyUsedLocalModel>(
boxName: 'Chicken_Widley_Box',
),
).called(1);
});
});
group('boxName', () {
test('should have correct box name', () {
// Assert
expect(chickenLocalDataSource.boxName, equals('Chicken_Widley_Box'));
});
});
});
}

View File

@@ -0,0 +1,350 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:rasadyar_chicken/data/data_source/remote/auth/auth_remote_imp.dart';
import 'package:rasadyar_chicken/data/models/response/user_info/user_info_model.dart';
import 'package:rasadyar_chicken/data/models/response/user_profile_model/user_profile_model.dart';
import 'package:rasadyar_core/core.dart';
import 'package:dio/dio.dart';
class MockDioRemote extends Mock implements DioRemote {}
void main() {
late AuthRemoteDataSourceImp authRemoteDataSource;
late MockDioRemote mockDioRemote;
setUpAll(() {
registerFallbackValue(<String, dynamic>{});
registerFallbackValue(<String, String>{});
});
setUp(() {
mockDioRemote = MockDioRemote();
authRemoteDataSource = AuthRemoteDataSourceImp(mockDioRemote);
});
group('AuthRemoteDataSourceImp', () {
group('login', () {
test('should return UserProfileModel when login is successful', () async {
// Arrange
final authRequest = {
'username': 'test@example.com',
'password': 'password',
};
final expectedUserProfile = UserProfileModel(
accessToken: 'test-access-token',
expiresIn: '3600',
scope: 'read write',
expireTime: '2024-12-31T23:59:59Z',
mobile: '09123456789',
fullname: 'John Doe',
firstname: 'John',
lastname: 'Doe',
city: 'Tehran',
province: 'Tehran',
nationalCode: '1234567890',
nationalId: '1234567890',
);
final mockResponse = DioResponse<UserProfileModel?>(
Response(
data: expectedUserProfile,
statusCode: 200,
requestOptions: RequestOptions(path: '/api/login/'),
),
);
when(
() => mockDioRemote.post<UserProfileModel?>(
'/api/login/',
data: authRequest,
fromJson: any(named: 'fromJson'),
headers: any(named: 'headers'),
),
).thenAnswer((_) async => mockResponse);
// Act
final result = await authRemoteDataSource.login(
authRequest: authRequest,
);
// Assert
expect(result, equals(expectedUserProfile));
verify(
() => mockDioRemote.post<UserProfileModel?>(
'/api/login/',
data: authRequest,
fromJson: UserProfileModel.fromJson,
headers: {'Content-Type': 'application/json'},
),
).called(1);
});
test('should return null when login fails', () async {
// Arrange
final authRequest = {
'username': 'test@example.com',
'password': 'wrong',
};
final mockResponse = DioResponse<UserProfileModel?>(
Response(
data: null,
statusCode: 401,
requestOptions: RequestOptions(path: '/api/login/'),
),
);
when(
() => mockDioRemote.post<UserProfileModel?>(
'/api/login/',
data: authRequest,
fromJson: any(named: 'fromJson'),
headers: any(named: 'headers'),
),
).thenAnswer((_) async => mockResponse);
// Act
final result = await authRemoteDataSource.login(
authRequest: authRequest,
);
// Assert
expect(result, isNull);
verify(
() => mockDioRemote.post<UserProfileModel?>(
'/api/login/',
data: authRequest,
fromJson: UserProfileModel.fromJson,
headers: {'Content-Type': 'application/json'},
),
).called(1);
});
});
group('hasAuthenticated', () {
test('should return true when user is authenticated', () async {
// Arrange
final mockResponse = DioResponse<bool>(
Response(
data: true,
statusCode: 200,
requestOptions: RequestOptions(path: 'auth/api/v1/login/'),
),
);
when(
() => mockDioRemote.get<bool>(
'auth/api/v1/login/',
headers: {'Content-Type': 'application/json'},
),
).thenAnswer((_) async => mockResponse);
// Act
final result = await authRemoteDataSource.hasAuthenticated();
// Assert
expect(result, isTrue);
verify(
() => mockDioRemote.get<bool>(
'auth/api/v1/login/',
headers: {'Content-Type': 'application/json'},
),
).called(1);
});
test('should return false when user is not authenticated', () async {
// Arrange
final mockResponse = DioResponse<bool>(
Response(
data: false,
statusCode: 401,
requestOptions: RequestOptions(path: 'auth/api/v1/login/'),
),
);
when(
() => mockDioRemote.get<bool>(
'auth/api/v1/login/',
headers: {'Content-Type': 'application/json'},
),
).thenAnswer((_) async => mockResponse);
// Act
final result = await authRemoteDataSource.hasAuthenticated();
// Assert
expect(result, isFalse);
verify(
() => mockDioRemote.get<bool>(
'auth/api/v1/login/',
headers: {'Content-Type': 'application/json'},
),
).called(1);
});
test('should return false when response data is null', () async {
// Arrange
final mockResponse = DioResponse<bool>(
Response(
data: null,
statusCode: 200,
requestOptions: RequestOptions(path: 'auth/api/v1/login/'),
),
);
when(
() => mockDioRemote.get<bool>(
'auth/api/v1/login/',
headers: {'Content-Type': 'application/json'},
),
).thenAnswer((_) async => mockResponse);
// Act
final result = await authRemoteDataSource.hasAuthenticated();
// Assert
expect(result, isFalse);
verify(
() => mockDioRemote.get<bool>(
'auth/api/v1/login/',
headers: {'Content-Type': 'application/json'},
),
).called(1);
});
});
group('getUserInfo', () {
test('should return UserInfoModel when user info is found', () async {
// Arrange
const phoneNumber = '09123456789';
final expectedUserInfo = UserInfoModel(
isUser: true,
address: 'Test Address',
backend: 'test-backend',
apiKey: 'test-api-key',
);
final mockResponse = DioResponse<UserInfoModel?>(
Response(
data: expectedUserInfo,
statusCode: 200,
requestOptions: RequestOptions(
path: 'https://userbackend.rasadyaar.ir/api/send_otp/',
),
),
);
when(
() => mockDioRemote.post<UserInfoModel?>(
'https://userbackend.rasadyaar.ir/api/send_otp/',
data: {"mobile": phoneNumber, "state": ""},
fromJson: any(named: 'fromJson'),
headers: any(named: 'headers'),
),
).thenAnswer((_) async => mockResponse);
// Act
final result = await authRemoteDataSource.getUserInfo(phoneNumber);
// Assert
expect(result, equals(expectedUserInfo));
verify(
() => mockDioRemote.post<UserInfoModel?>(
'https://userbackend.rasadyaar.ir/api/send_otp/',
data: {"mobile": phoneNumber, "state": ""},
fromJson: UserInfoModel.fromJson,
headers: {'Content-Type': 'application/json'},
),
).called(1);
});
test('should return null when user info is not found', () async {
// Arrange
const phoneNumber = '09123456789';
final mockResponse = DioResponse<UserInfoModel?>(
Response(
data: null,
statusCode: 404,
requestOptions: RequestOptions(
path: 'https://userbackend.rasadyaar.ir/api/send_otp/',
),
),
);
when(
() => mockDioRemote.post<UserInfoModel?>(
'https://userbackend.rasadyaar.ir/api/send_otp/',
data: {"mobile": phoneNumber, "state": ""},
fromJson: any(named: 'fromJson'),
headers: any(named: 'headers'),
),
).thenAnswer((_) async => mockResponse);
// Act
final result = await authRemoteDataSource.getUserInfo(phoneNumber);
// Assert
expect(result, isNull);
verify(
() => mockDioRemote.post<UserInfoModel?>(
'https://userbackend.rasadyaar.ir/api/send_otp/',
data: {"mobile": phoneNumber, "state": ""},
fromJson: UserInfoModel.fromJson,
headers: {'Content-Type': 'application/json'},
),
).called(1);
});
});
group('submitUserInfo', () {
test(
'should call remote submitUserInfo with correct parameters',
() async {
// Arrange
final userInfo = {
'mobile': '09123456789',
'device_name': 'Test Device',
};
final mockResponse = DioResponse<dynamic>(
Response(
data: null,
statusCode: 200,
requestOptions: RequestOptions(path: '/steward-app-login/'),
),
);
when(
() => mockDioRemote.post(
'/steward-app-login/',
data: userInfo,
headers: any(named: 'headers'),
),
).thenAnswer((_) async => mockResponse);
// Act
await authRemoteDataSource.submitUserInfo(userInfo);
// Assert
verify(
() => mockDioRemote.post(
'/steward-app-login/',
data: userInfo,
headers: {'Content-Type': 'application/json'},
),
).called(1);
},
);
});
group('logout', () {
test('should throw UnimplementedError', () async {
// Act & Assert
expect(
() => authRemoteDataSource.logout(),
throwsA(isA<UnimplementedError>()),
);
});
});
});
}

View File

@@ -4,10 +4,26 @@ import 'package:rasadyar_chicken/data/di/chicken_di.dart';
import 'package:rasadyar_core/core.dart';
void main() {
setUpAll(() {
// Mock platform services for testing
TestWidgetsFlutterBinding.ensureInitialized();
});
setUp(() async {
await setupAllCoreProvider();
Get.put(TokenStorageService());
await setupChickenDI();
// Skip platform-dependent setup for unit tests
try {
await setupAllCoreProvider();
Get.put(TokenStorageService());
await setupChickenDI();
} catch (e) {
// Mock the setup for testing - just register the service manually
print('Mocking platform services for testing: $e');
if (!diChicken.isRegistered<DioErrorHandler>()) {
diChicken.registerLazySingleton<DioErrorHandler>(
() => DioErrorHandler(),
);
}
}
});
group('Check class type registered', () {

View File

@@ -0,0 +1,158 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:rasadyar_chicken/data/models/response/user_profile_model/user_profile_model.dart';
void main() {
group('UserProfileModel', () {
test('should create UserProfileModel with all properties', () {
// Arrange & Act
final userProfile = UserProfileModel(
accessToken: 'test-access-token',
expiresIn: '3600',
scope: 'read write',
expireTime: '2024-12-31T23:59:59Z',
mobile: '09123456789',
fullname: 'John Doe',
firstname: 'John',
lastname: 'Doe',
city: 'Tehran',
province: 'Tehran',
nationalCode: '1234567890',
nationalId: '1234567890',
birthday: '1990-01-01',
image: 'https://example.com/image.jpg',
baseOrder: 1,
role: ['admin', 'user'],
);
// Assert
expect(userProfile.accessToken, equals('test-access-token'));
expect(userProfile.expiresIn, equals('3600'));
expect(userProfile.scope, equals('read write'));
expect(userProfile.expireTime, equals('2024-12-31T23:59:59Z'));
expect(userProfile.mobile, equals('09123456789'));
expect(userProfile.fullname, equals('John Doe'));
expect(userProfile.firstname, equals('John'));
expect(userProfile.lastname, equals('Doe'));
expect(userProfile.city, equals('Tehran'));
expect(userProfile.province, equals('Tehran'));
expect(userProfile.nationalCode, equals('1234567890'));
expect(userProfile.nationalId, equals('1234567890'));
expect(userProfile.birthday, equals('1990-01-01'));
expect(userProfile.image, equals('https://example.com/image.jpg'));
expect(userProfile.baseOrder, equals(1));
expect(userProfile.role, equals(['admin', 'user']));
});
test('should create UserProfileModel with minimal properties', () {
// Arrange & Act
const userProfile = UserProfileModel();
// Assert
expect(userProfile.accessToken, isNull);
expect(userProfile.expiresIn, isNull);
expect(userProfile.scope, isNull);
expect(userProfile.expireTime, isNull);
expect(userProfile.mobile, isNull);
expect(userProfile.fullname, isNull);
expect(userProfile.firstname, isNull);
expect(userProfile.lastname, isNull);
expect(userProfile.city, isNull);
expect(userProfile.province, isNull);
expect(userProfile.nationalCode, isNull);
expect(userProfile.nationalId, isNull);
expect(userProfile.birthday, isNull);
expect(userProfile.image, isNull);
expect(userProfile.baseOrder, isNull);
expect(userProfile.role, isNull);
});
test('should create UserProfileModel with partial properties', () {
// Arrange & Act
final userProfile = UserProfileModel(
mobile: '09123456789',
firstname: 'John',
lastname: 'Doe',
role: ['user'],
);
// Assert
expect(userProfile.accessToken, isNull);
expect(userProfile.mobile, equals('09123456789'));
expect(userProfile.firstname, equals('John'));
expect(userProfile.lastname, equals('Doe'));
expect(userProfile.role, equals(['user']));
expect(userProfile.city, isNull);
expect(userProfile.province, isNull);
});
test('should support equality comparison', () {
// Arrange
final userProfile1 = UserProfileModel(
mobile: '09123456789',
firstname: 'John',
lastname: 'Doe',
role: ['user'],
);
final userProfile2 = UserProfileModel(
mobile: '09123456789',
firstname: 'John',
lastname: 'Doe',
role: ['user'],
);
final userProfile3 = UserProfileModel(
mobile: '09123456789',
firstname: 'Jane',
lastname: 'Doe',
role: ['user'],
);
// Assert
expect(userProfile1, equals(userProfile2));
expect(userProfile1, isNot(equals(userProfile3)));
});
test('should support copyWith method', () {
// Arrange
final originalProfile = UserProfileModel(
mobile: '09123456789',
firstname: 'John',
lastname: 'Doe',
role: ['user'],
);
// Act
final updatedProfile = originalProfile.copyWith(
firstname: 'Jane',
city: 'Tehran',
);
// Assert
expect(updatedProfile.mobile, equals('09123456789'));
expect(updatedProfile.firstname, equals('Jane'));
expect(updatedProfile.lastname, equals('Doe'));
expect(updatedProfile.city, equals('Tehran'));
expect(updatedProfile.role, equals(['user']));
expect(updatedProfile.province, isNull);
});
test('should support toString method', () {
// Arrange
final userProfile = UserProfileModel(
mobile: '09123456789',
firstname: 'John',
lastname: 'Doe',
);
// Act
final stringRepresentation = userProfile.toString();
// Assert
expect(stringRepresentation, contains('UserProfileModel'));
expect(stringRepresentation, contains('mobile: 09123456789'));
expect(stringRepresentation, contains('firstname: John'));
expect(stringRepresentation, contains('lastname: Doe'));
});
});
}

View File

@@ -0,0 +1,190 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:rasadyar_chicken/data/data_source/remote/auth/auth_remote.dart';
import 'package:rasadyar_chicken/data/models/response/user_info/user_info_model.dart';
import 'package:rasadyar_chicken/data/models/response/user_profile_model/user_profile_model.dart';
import 'package:rasadyar_chicken/data/repositories/auth/auth_repository_imp.dart';
class MockAuthRemoteDataSource extends Mock implements AuthRemoteDataSource {}
void main() {
late AuthRepositoryImpl authRepository;
late MockAuthRemoteDataSource mockAuthRemote;
setUp(() {
mockAuthRemote = MockAuthRemoteDataSource();
authRepository = AuthRepositoryImpl(mockAuthRemote);
});
group('AuthRepositoryImpl', () {
group('login', () {
test('should return UserProfileModel when login is successful', () async {
// Arrange
final authRequest = {
'username': 'test@example.com',
'password': 'password',
};
final expectedUserProfile = UserProfileModel(
accessToken: 'test-access-token',
firstname: 'John',
lastname: 'Doe',
mobile: '09123456789',
);
when(
() => mockAuthRemote.login(authRequest: authRequest),
).thenAnswer((_) async => expectedUserProfile);
// Act
final result = await authRepository.login(authRequest: authRequest);
// Assert
expect(result, equals(expectedUserProfile));
verify(() => mockAuthRemote.login(authRequest: authRequest)).called(1);
});
test('should return null when login fails', () async {
// Arrange
final authRequest = {
'username': 'test@example.com',
'password': 'wrong',
};
when(
() => mockAuthRemote.login(authRequest: authRequest),
).thenAnswer((_) async => null);
// Act
final result = await authRepository.login(authRequest: authRequest);
// Assert
expect(result, isNull);
verify(() => mockAuthRemote.login(authRequest: authRequest)).called(1);
});
});
group('logout', () {
test('should call remote logout method', () async {
// Arrange
when(() => mockAuthRemote.logout()).thenAnswer((_) async {});
// Act
await authRepository.logout();
// Assert
verify(() => mockAuthRemote.logout()).called(1);
});
});
group('hasAuthenticated', () {
test('should return true when user is authenticated', () async {
// Arrange
when(
() => mockAuthRemote.hasAuthenticated(),
).thenAnswer((_) async => true);
// Act
final result = await authRepository.hasAuthenticated();
// Assert
expect(result, isTrue);
verify(() => mockAuthRemote.hasAuthenticated()).called(1);
});
test('should return false when user is not authenticated', () async {
// Arrange
when(
() => mockAuthRemote.hasAuthenticated(),
).thenAnswer((_) async => false);
// Act
final result = await authRepository.hasAuthenticated();
// Assert
expect(result, isFalse);
verify(() => mockAuthRemote.hasAuthenticated()).called(1);
});
});
group('getUserInfo', () {
test('should return UserInfoModel when user info is found', () async {
// Arrange
const phoneNumber = '09123456789';
final expectedUserInfo = UserInfoModel(
isUser: true,
address: 'Test Address',
backend: 'test-backend',
apiKey: 'test-api-key',
);
when(
() => mockAuthRemote.getUserInfo(phoneNumber),
).thenAnswer((_) async => expectedUserInfo);
// Act
final result = await authRepository.getUserInfo(phoneNumber);
// Assert
expect(result, equals(expectedUserInfo));
verify(() => mockAuthRemote.getUserInfo(phoneNumber)).called(1);
});
test('should return null when user info is not found', () async {
// Arrange
const phoneNumber = '09123456789';
when(
() => mockAuthRemote.getUserInfo(phoneNumber),
).thenAnswer((_) async => null);
// Act
final result = await authRepository.getUserInfo(phoneNumber);
// Assert
expect(result, isNull);
verify(() => mockAuthRemote.getUserInfo(phoneNumber)).called(1);
});
});
group('submitUserInfo', () {
test(
'should call remote submitUserInfo with correct parameters',
() async {
// Arrange
const phone = '09123456789';
const deviceName = 'Test Device';
final expectedData = {'mobile': phone, 'device_name': deviceName};
when(
() => mockAuthRemote.submitUserInfo(any()),
).thenAnswer((_) async {});
// Act
await authRepository.submitUserInfo(
phone: phone,
deviceName: deviceName,
);
// Assert
verify(() => mockAuthRemote.submitUserInfo(expectedData)).called(1);
},
);
test('should call remote submitUserInfo without device name', () async {
// Arrange
const phone = '09123456789';
final expectedData = {'mobile': phone, 'device_name': null};
when(
() => mockAuthRemote.submitUserInfo(any()),
).thenAnswer((_) async {});
// Act
await authRepository.submitUserInfo(phone: phone);
// Assert
verify(() => mockAuthRemote.submitUserInfo(expectedData)).called(1);
});
});
});
}

View File

@@ -0,0 +1,10 @@
import 'package:flutter_test/flutter_test.dart';
void main() {
group('ChickenRepositoryImp', () {
test('should be implemented', () {
// TODO: Implement chicken repository tests
expect(true, isTrue);
});
});
}

View File

@@ -0,0 +1,276 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:rasadyar_chicken/data/data_source/remote/auth/auth_remote.dart';
import 'package:rasadyar_chicken/data/models/response/user_info/user_info_model.dart';
import 'package:rasadyar_chicken/data/models/response/user_profile_model/user_profile_model.dart';
import 'package:rasadyar_chicken/data/repositories/auth/auth_repository_imp.dart';
import 'package:rasadyar_core/core.dart';
class MockAuthRemoteDataSource extends Mock implements AuthRemoteDataSource {}
void main() {
late AuthRepositoryImpl authRepository;
late MockAuthRemoteDataSource mockAuthRemote;
setUp(() {
mockAuthRemote = MockAuthRemoteDataSource();
authRepository = AuthRepositoryImpl(mockAuthRemote);
});
group('Authentication Flow Integration Tests', () {
group('Complete Login Flow', () {
test('should complete full login flow successfully', () async {
// Arrange
const phoneNumber = '09123456789';
const deviceName = 'Test Device';
final authRequest = {
'username': 'test@example.com',
'password': 'password',
};
final expectedUserInfo = UserInfoModel(
isUser: true,
address: 'Test Address',
backend: 'test-backend',
apiKey: 'test-api-key',
);
final expectedUserProfile = UserProfileModel(
accessToken: 'access-token',
expiresIn: '3600',
scope: 'read write',
expireTime: '2024-12-31T23:59:59Z',
mobile: phoneNumber,
fullname: 'John Doe',
firstname: 'John',
lastname: 'Doe',
);
// Mock the flow
when(
() => mockAuthRemote.getUserInfo(phoneNumber),
).thenAnswer((_) async => expectedUserInfo);
when(
() => mockAuthRemote.submitUserInfo(any()),
).thenAnswer((_) async {});
when(
() => mockAuthRemote.login(authRequest: authRequest),
).thenAnswer((_) async => expectedUserProfile);
// Act - Step 1: Get user info
final userInfo = await authRepository.getUserInfo(phoneNumber);
expect(userInfo, equals(expectedUserInfo));
// Act - Step 2: Submit user info
await authRepository.submitUserInfo(
phone: phoneNumber,
deviceName: deviceName,
);
// Act - Step 3: Login
final userProfile = await authRepository.login(
authRequest: authRequest,
);
// Assert
expect(userProfile, equals(expectedUserProfile));
verify(() => mockAuthRemote.getUserInfo(phoneNumber)).called(1);
verify(() => mockAuthRemote.submitUserInfo(any())).called(1);
verify(() => mockAuthRemote.login(authRequest: authRequest)).called(1);
});
test('should handle login flow with authentication check', () async {
// Arrange
final authRequest = {
'username': 'test@example.com',
'password': 'password',
};
final expectedUserProfile = UserProfileModel(
accessToken: 'access-token',
expiresIn: '3600',
scope: 'read write',
expireTime: '2024-12-31T23:59:59Z',
mobile: '09123456789',
fullname: 'John Doe',
firstname: 'John',
lastname: 'Doe',
);
// Mock the flow
when(
() => mockAuthRemote.hasAuthenticated(),
).thenAnswer((_) async => false);
when(
() => mockAuthRemote.login(authRequest: authRequest),
).thenAnswer((_) async => expectedUserProfile);
// Act - Step 1: Check authentication status
final isAuthenticated = await authRepository.hasAuthenticated();
expect(isAuthenticated, isFalse);
// Act - Step 2: Login
final userProfile = await authRepository.login(
authRequest: authRequest,
);
// Assert
expect(userProfile, equals(expectedUserProfile));
verify(() => mockAuthRemote.hasAuthenticated()).called(1);
verify(() => mockAuthRemote.login(authRequest: authRequest)).called(1);
});
});
group('Error Handling in Authentication Flow', () {
test('should handle user info retrieval failure', () async {
// Arrange
const phoneNumber = '09123456789';
when(
() => mockAuthRemote.getUserInfo(phoneNumber),
).thenAnswer((_) async => null);
// Act
final userInfo = await authRepository.getUserInfo(phoneNumber);
// Assert
expect(userInfo, isNull);
verify(() => mockAuthRemote.getUserInfo(phoneNumber)).called(1);
});
test('should handle login failure after successful user info', () async {
// Arrange
const phoneNumber = '09123456789';
const deviceName = 'Test Device';
final authRequest = {
'username': 'test@example.com',
'password': 'wrong',
};
final expectedUserInfo = UserInfoModel(
isUser: true,
address: 'Test Address',
backend: 'test-backend',
apiKey: 'test-api-key',
);
// Mock the flow
when(
() => mockAuthRemote.getUserInfo(phoneNumber),
).thenAnswer((_) async => expectedUserInfo);
when(
() => mockAuthRemote.submitUserInfo(any()),
).thenAnswer((_) async {});
when(
() => mockAuthRemote.login(authRequest: authRequest),
).thenAnswer((_) async => null);
// Act - Step 1: Get user info (success)
final userInfo = await authRepository.getUserInfo(phoneNumber);
expect(userInfo, equals(expectedUserInfo));
// Act - Step 2: Submit user info (success)
await authRepository.submitUserInfo(
phone: phoneNumber,
deviceName: deviceName,
);
// Act - Step 3: Login (failure)
final userProfile = await authRepository.login(
authRequest: authRequest,
);
// Assert
expect(userProfile, isNull);
verify(() => mockAuthRemote.getUserInfo(phoneNumber)).called(1);
verify(() => mockAuthRemote.submitUserInfo(any())).called(1);
verify(() => mockAuthRemote.login(authRequest: authRequest)).called(1);
});
});
group('Logout Flow', () {
test('should complete logout flow', () async {
// Arrange
when(() => mockAuthRemote.logout()).thenAnswer((_) async {});
// Act
await authRepository.logout();
// Assert
verify(() => mockAuthRemote.logout()).called(1);
});
});
group('Authentication State Management', () {
test('should track authentication state correctly', () async {
// Arrange
when(
() => mockAuthRemote.hasAuthenticated(),
).thenAnswer((_) async => true);
// Act
final isAuthenticated = await authRepository.hasAuthenticated();
// Assert
expect(isAuthenticated, isTrue);
verify(() => mockAuthRemote.hasAuthenticated()).called(1);
});
test('should handle authentication state changes', () async {
// Arrange
when(
() => mockAuthRemote.hasAuthenticated(),
).thenAnswer((_) async => false);
// Act
final isAuthenticated = await authRepository.hasAuthenticated();
// Assert
expect(isAuthenticated, isFalse);
verify(() => mockAuthRemote.hasAuthenticated()).called(1);
});
});
group('User Info Management', () {
test('should handle user info submission without device name', () async {
// Arrange
const phone = '09123456789';
final expectedData = {'mobile': phone, 'device_name': null};
when(
() => mockAuthRemote.submitUserInfo(any()),
).thenAnswer((_) async {});
// Act
await authRepository.submitUserInfo(phone: phone);
// Assert
verify(() => mockAuthRemote.submitUserInfo(expectedData)).called(1);
});
test('should handle user info submission with device name', () async {
// Arrange
const phone = '09123456789';
const deviceName = 'Test Device';
final expectedData = {'mobile': phone, 'device_name': deviceName};
when(
() => mockAuthRemote.submitUserInfo(any()),
).thenAnswer((_) async {});
// Act
await authRepository.submitUserInfo(
phone: phone,
deviceName: deviceName,
);
// Assert
verify(() => mockAuthRemote.submitUserInfo(expectedData)).called(1);
});
});
});
}

View File

@@ -0,0 +1,641 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:rasadyar_chicken/data/data_source/remote/poultry_science/poultry_science_remote.dart';
import 'package:rasadyar_chicken/data/models/poultry_export/poultry_export.dart';
import 'package:rasadyar_chicken/data/models/request/kill_registration/kill_registration.dart';
import 'package:rasadyar_chicken/data/models/response/all_poultry/all_poultry.dart';
import 'package:rasadyar_chicken/data/models/response/approved_price/approved_price.dart';
import 'package:rasadyar_chicken/data/models/response/hatching/hatching_models.dart';
import 'package:rasadyar_chicken/data/models/response/hatching_report/hatching_report.dart';
import 'package:rasadyar_chicken/data/models/response/kill_house_poultry/kill_house_poultry.dart';
import 'package:rasadyar_chicken/data/models/response/kill_request_poultry/kill_request_poultry.dart';
import 'package:rasadyar_chicken/data/models/response/poultry_farm/poultry_farm.dart';
import 'package:rasadyar_chicken/data/models/response/poultry_hatching/poultry_hatching.dart';
import 'package:rasadyar_chicken/data/models/response/poultry_order/poultry_order.dart';
import 'package:rasadyar_chicken/data/models/response/poultry_science/home_poultry_science/home_poultry_science_model.dart';
import 'package:rasadyar_chicken/data/models/response/sell_for_freezing/sell_for_freezing.dart';
import 'package:rasadyar_chicken/data/repositories/poultry_science/poultry_science_repository_imp.dart';
import 'package:rasadyar_core/core.dart';
class MockPoultryScienceRemoteDataSource extends Mock
implements PoultryScienceRemoteDatasource {}
void main() {
late PoultryScienceRepositoryImp poultryScienceRepository;
late MockPoultryScienceRemoteDataSource mockRemote;
setUp(() {
mockRemote = MockPoultryScienceRemoteDataSource();
poultryScienceRepository = PoultryScienceRepositoryImp(mockRemote);
});
group('Poultry Science Integration Tests', () {
const token = 'test-token';
group('Complete Poultry Science Home Flow', () {
test('should complete full poultry science home workflow', () async {
// Arrange
const type = 'hatching';
final expectedHomeModel = HomePoultryScienceModel(
farmCount: 5,
hatchingCount: 1000,
hatchingQuantity: 500,
hatchingLeftOver: 200,
hatchingLosses: 50,
hatchingKilledQuantity: 250,
hatchingMaxAge: 45,
hatchingMinAge: 30,
);
final expectedHatching = [
HatchingModel(
id: 1,
key: 'hatching-1',
date: '2024-01-01',
quantity: 100,
state: 'active',
),
];
final expectedHatchingPagination = PaginationModel<HatchingModel>(
results: expectedHatching,
count: 1,
next: null,
previous: null,
);
// Mock the flow
when(
() => mockRemote.getHomePoultryScience(token: token, type: type),
).thenAnswer((_) async => expectedHomeModel);
when(
() => mockRemote.getHatchingPoultry(
token: token,
queryParameters: any(named: 'queryParameters'),
),
).thenAnswer((_) async => expectedHatchingPagination);
// Act - Step 1: Get home poultry science data
final homeModel = await poultryScienceRepository.getHomePoultry(
token: token,
type: type,
);
// Act - Step 2: Get hatching poultry data
final hatchingData = await poultryScienceRepository.getHatchingPoultry(
token: token,
queryParameters: {'page': '1', 'limit': '10'},
);
// Assert
expect(homeModel, equals(expectedHomeModel));
expect(hatchingData, equals(expectedHatchingPagination));
verify(
() => mockRemote.getHomePoultryScience(token: token, type: type),
).called(1);
verify(
() => mockRemote.getHatchingPoultry(
token: token,
queryParameters: any(named: 'queryParameters'),
),
).called(1);
});
});
group('Hatching Report Management Flow', () {
test('should complete hatching report management workflow', () async {
// Arrange
final queryParameters = {'page': '1', 'limit': '10'};
final expectedReports = [
HatchingReport(
id: 1,
key: 'report-1',
date: DateTime.parse('2024-01-01'),
state: 'completed',
),
];
final expectedPagination = PaginationModel<HatchingReport>(
results: expectedReports,
count: 1,
next: null,
previous: null,
);
final mockFormData = MockFormData();
// Mock the flow
when(
() => mockRemote.getPoultryScienceReport(
token: token,
queryParameters: queryParameters,
),
).thenAnswer((_) async => expectedPagination);
when(
() => mockRemote.submitPoultryScienceReport(
token: token,
data: mockFormData,
onSendProgress: any(named: 'onSendProgress'),
),
).thenAnswer((_) async {});
// Act - Step 1: Get hatching reports
final reports = await poultryScienceRepository.getHatchingPoultryReport(
token: token,
queryParameters: queryParameters,
);
// Act - Step 2: Submit new report
await poultryScienceRepository.submitPoultryScienceReport(
token: token,
data: mockFormData,
);
// Assert
expect(reports, equals(expectedPagination));
verify(
() => mockRemote.getPoultryScienceReport(
token: token,
queryParameters: queryParameters,
),
).called(1);
verify(
() => mockRemote.submitPoultryScienceReport(
token: token,
data: mockFormData,
onSendProgress: any(named: 'onSendProgress'),
),
).called(1);
});
});
group('Poultry Farm Management Flow', () {
test('should complete poultry farm management workflow', () async {
// Arrange
final queryParameters = {'page': '1', 'limit': '10'};
final expectedFarms = [
PoultryFarm(
id: 1,
key: 'farm-1',
unitName: 'Farm 1',
totalCapacity: 1000,
cityName: 'Tehran',
),
];
final expectedPagination = PaginationModel<PoultryFarm>(
results: expectedFarms,
count: 1,
next: null,
previous: null,
);
// Mock the flow
when(
() => mockRemote.getPoultryScienceFarmList(
token: token,
queryParameters: queryParameters,
),
).thenAnswer((_) async => expectedPagination);
// Act
final farms = await poultryScienceRepository.getPoultryScienceFarmList(
token: token,
queryParameters: queryParameters,
);
// Assert
expect(farms, equals(expectedPagination));
verify(
() => mockRemote.getPoultryScienceFarmList(
token: token,
queryParameters: queryParameters,
),
).called(1);
});
});
group('Pricing and Market Data Flow', () {
test('should complete pricing and market data workflow', () async {
// Arrange
final queryParameters = {'date': '2024-01-01'};
final expectedApprovedPrice = ApprovedPrice(
approved: true,
lowestPrice: 45000.0,
highestPrice: 55000.0,
lowestWeight: 1.5,
highestWeight: 2.5,
);
final expectedSellForFreezing = SellForFreezing(permission: true);
final expectedPoultryExport = PoultryExport(
key: 'export-key',
allow: true,
limitationStatus: false,
limitation: 100.0,
);
// Mock the flow
when(
() => mockRemote.getApprovedPrice(
token: token,
queryParameters: queryParameters,
),
).thenAnswer((_) async => expectedApprovedPrice);
when(
() => mockRemote.getSellForFreezing(
token: token,
queryParameters: queryParameters,
),
).thenAnswer((_) async => expectedSellForFreezing);
when(
() => mockRemote.getPoultryExport(
token: token,
queryParameters: queryParameters,
),
).thenAnswer((_) async => expectedPoultryExport);
// Act - Step 1: Get approved price
final approvedPrice = await poultryScienceRepository.getApprovedPrice(
token: token,
queryParameters: queryParameters,
);
// Act - Step 2: Get sell for freezing data
final sellForFreezing = await poultryScienceRepository
.getSellForFreezing(token: token, queryParameters: queryParameters);
// Act - Step 3: Get poultry export data
final poultryExport = await poultryScienceRepository.getPoultryExport(
token: token,
queryParameters: queryParameters,
);
// Assert
expect(approvedPrice, equals(expectedApprovedPrice));
expect(sellForFreezing, equals(expectedSellForFreezing));
expect(poultryExport, equals(expectedPoultryExport));
verify(
() => mockRemote.getApprovedPrice(
token: token,
queryParameters: queryParameters,
),
).called(1);
verify(
() => mockRemote.getSellForFreezing(
token: token,
queryParameters: queryParameters,
),
).called(1);
verify(
() => mockRemote.getPoultryExport(
token: token,
queryParameters: queryParameters,
),
).called(1);
});
});
group('Kill Registration Flow', () {
test('should complete kill registration workflow', () async {
// Arrange
final queryParameters = {'page': '1', 'limit': '10'};
final expectedKillRequests = [
KillRequestPoultry(
key: 'kill-request-1',
unitName: 'Farm 1',
totalCapacity: 1000,
cityName: 'Tehran',
provinceName: 'Tehran',
),
];
final expectedKillHouses = [
KillHousePoultry(
name: 'Kill House 1',
killer: true,
fullname: 'Kill House Manager',
quantitySum: 500,
firstQuantity: 100,
poultryQuantitySum: 400,
killReqKey: 'killhouse-1',
),
];
final expectedPoultryHatching = [
PoultryHatching(
key: 'hatching-1',
quantity: 100,
losses: 5,
leftOver: 95,
killedQuantity: 50,
state: 'active',
date: '2024-01-01',
age: 30,
),
];
final killRegistrationRequest = KillRegistrationRequest(
killReqKey: 'registration-key',
operatorKey: 'operator-1',
poultryHatchingKey: 'hatching-1',
quantity: 100,
sendDate: '2024-01-01',
chickenBreed: 'Broiler',
indexWeight: 2.0,
losses: '5',
freezing: false,
export: false,
cash: true,
credit: false,
role: 'farmer',
poultryKey: 'poultry-1',
amount: 100000,
financialOperation: 'cash',
freeSaleInProvince: true,
confirmPoultryMobile: '09123456789',
);
// Mock the flow
when(
() => mockRemote.getUserPoultry(
token: token,
queryParameters: queryParameters,
),
).thenAnswer(
(_) async => expectedKillRequests.cast<KillRequestPoultry>(),
);
when(
() => mockRemote.getKillHouseList(
token: token,
queryParameters: queryParameters,
),
).thenAnswer((_) async => expectedKillHouses.cast<KillHousePoultry>());
when(
() => mockRemote.getPoultryHatching(
token: token,
queryParameters: queryParameters,
),
).thenAnswer(
(_) async => expectedPoultryHatching.cast<PoultryHatching>(),
);
when(
() => mockRemote.submitKillRegistration(
token: token,
request: killRegistrationRequest,
),
).thenAnswer((_) async {});
// Act - Step 1: Get user poultry
final killRequests = await poultryScienceRepository.getUserPoultry(
token: token,
queryParameters: queryParameters,
);
// Act - Step 2: Get kill house list
final killHouses = await poultryScienceRepository.getKillHouseList(
token: token,
queryParameters: queryParameters,
);
// Act - Step 3: Get poultry hatching
final poultryHatching = await poultryScienceRepository
.getPoultryHatching(token: token, queryParameters: queryParameters);
// Act - Step 4: Submit kill registration
await poultryScienceRepository.submitKillRegistration(
token: token,
request: killRegistrationRequest,
);
// Assert
expect(killRequests, equals(expectedKillRequests));
expect(killHouses, equals(expectedKillHouses));
expect(poultryHatching, equals(expectedPoultryHatching));
verify(
() => mockRemote.getUserPoultry(
token: token,
queryParameters: queryParameters,
),
).called(1);
verify(
() => mockRemote.getKillHouseList(
token: token,
queryParameters: queryParameters,
),
).called(1);
verify(
() => mockRemote.getPoultryHatching(
token: token,
queryParameters: queryParameters,
),
).called(1);
verify(
() => mockRemote.submitKillRegistration(
token: token,
request: killRegistrationRequest,
),
).called(1);
});
});
group('Poultry Order Management Flow', () {
test('should complete poultry order management workflow', () async {
// Arrange
final queryParameters = {'page': '1', 'limit': '10'};
const orderId = 'order-1';
final expectedOrders = [
PoultryOrder(
key: 'order-1',
id: 1,
orderCode: 1001,
createDate: '2024-01-01',
sendDate: '2024-01-02',
quantity: 100,
firstQuantity: 100,
amount: 5000000.0,
finalState: 'pending',
provinceState: 'pending',
stateProcess: 'processing',
freeSaleInProvince: true,
freezing: false,
export: false,
market: true,
),
];
final expectedPagination = PaginationModel<PoultryOrder>(
count: 1,
next: null,
previous: null,
results: expectedOrders,
);
// Mock the flow
when(
() => mockRemote.getPoultryOderList(
token: token,
queryParameters: queryParameters,
),
).thenAnswer((_) async => expectedPagination);
when(
() => mockRemote.deletePoultryOder(token: token, orderId: orderId),
).thenAnswer((_) async {});
// Act - Step 1: Get poultry orders
final orders = await poultryScienceRepository.getPoultryOderList(
token: token,
queryParameters: queryParameters,
);
// Act - Step 2: Delete poultry order
await poultryScienceRepository.deletePoultryOder(
token: token,
orderId: orderId,
);
// Assert
expect(orders, equals(expectedPagination));
verify(
() => mockRemote.getPoultryOderList(
token: token,
queryParameters: queryParameters,
),
).called(1);
verify(
() => mockRemote.deletePoultryOder(token: token, orderId: orderId),
).called(1);
});
});
group('All Poultry Data Flow', () {
test('should complete all poultry data retrieval workflow', () async {
// Arrange
final queryParameters = {'type': 'all'};
final expectedAllPoultry = [
AllPoultry(
key: 'poultry-1',
unitName: 'Poultry Farm 1',
lastHatchingRemainQuantity: 100,
provinceAllowSellFree: true,
),
];
// Mock the flow
when(
() => mockRemote.getAllPoultry(
token: token,
queryParameters: queryParameters,
),
).thenAnswer((_) async => expectedAllPoultry.cast<AllPoultry>());
// Act
final allPoultry = await poultryScienceRepository.getAllPoultry(
token: token,
queryParameters: queryParameters,
);
// Assert
expect(allPoultry, equals(expectedAllPoultry));
verify(
() => mockRemote.getAllPoultry(
token: token,
queryParameters: queryParameters,
),
).called(1);
});
});
group('Error Handling in Poultry Science Workflow', () {
test('should handle home poultry data retrieval failure', () async {
// Arrange
const type = 'hatching';
when(
() => mockRemote.getHomePoultryScience(token: token, type: type),
).thenAnswer((_) async => null);
// Act
final homeModel = await poultryScienceRepository.getHomePoultry(
token: token,
type: type,
);
// Assert
expect(homeModel, isNull);
verify(
() => mockRemote.getHomePoultryScience(token: token, type: type),
).called(1);
});
test('should handle kill registration submission failure', () async {
// Arrange
final killRegistrationRequest = KillRegistrationRequest(
killReqKey: 'registration-key',
operatorKey: 'operator-1',
poultryHatchingKey: 'hatching-1',
quantity: 100,
sendDate: '2024-01-01',
chickenBreed: 'Broiler',
indexWeight: 2.0,
losses: '5',
freezing: false,
export: false,
cash: true,
credit: false,
role: 'farmer',
poultryKey: 'poultry-1',
amount: 100000,
financialOperation: 'cash',
freeSaleInProvince: true,
confirmPoultryMobile: '09123456789',
);
when(
() => mockRemote.submitKillRegistration(
token: token,
request: killRegistrationRequest,
),
).thenThrow(Exception('Kill registration submission failed'));
// Act & Assert
expect(
() => poultryScienceRepository.submitKillRegistration(
token: token,
request: killRegistrationRequest,
),
throwsA(isA<Exception>()),
);
verify(
() => mockRemote.submitKillRegistration(
token: token,
request: killRegistrationRequest,
),
).called(1);
});
});
});
}
// Mock FormData class
class MockFormData extends Mock implements FormData {}

View File

@@ -0,0 +1,469 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:rasadyar_chicken/data/data_source/local/chicken_local.dart';
import 'package:rasadyar_chicken/data/data_source/remote/chicken/chicken_remote.dart';
import 'package:rasadyar_chicken/data/models/local/widely_used_local_model.dart';
import 'package:rasadyar_chicken/data/models/request/create_steward_free_bar/create_steward_free_bar.dart';
import 'package:rasadyar_chicken/data/models/request/steward_free_sale_bar/steward_free_sale_bar_request.dart';
import 'package:rasadyar_chicken/data/models/request/submit_steward_allocation/submit_steward_allocation.dart';
import 'package:rasadyar_chicken/data/models/response/allocated_made/allocated_made.dart';
import 'package:rasadyar_chicken/data/models/response/bar_information/bar_information.dart';
import 'package:rasadyar_chicken/data/models/response/broadcast_price/broadcast_price.dart';
import 'package:rasadyar_chicken/data/models/response/dashboard_kill_house_free_bar/dashboard_kill_house_free_bar.dart';
import 'package:rasadyar_chicken/data/models/response/guild/guild_model.dart';
import 'package:rasadyar_chicken/data/models/response/guild_profile/guild_profile.dart';
import 'package:rasadyar_chicken/data/models/response/inventory/inventory_model.dart';
import 'package:rasadyar_chicken/data/models/response/iran_province_city/iran_province_city_model.dart';
import 'package:rasadyar_chicken/data/models/response/kill_house_poultry/kill_house_poultry.dart';
import 'package:rasadyar_chicken/data/models/response/kill_house_distribution_info/kill_house_distribution_info.dart';
import 'package:rasadyar_chicken/data/models/response/out_province_carcasses_buyer/out_province_carcasses_buyer.dart';
import 'package:rasadyar_chicken/data/models/response/roles_products/roles_products.dart';
import 'package:rasadyar_chicken/data/models/response/segmentation_model/segmentation_model.dart';
import 'package:rasadyar_chicken/data/models/response/steward_free_bar/steward_free_bar.dart';
import 'package:rasadyar_chicken/data/models/response/steward_free_bar_dashboard/steward_free_bar_dashboard.dart';
import 'package:rasadyar_chicken/data/models/response/steward_free_sale_bar/steward_free_sale_bar.dart';
import 'package:rasadyar_chicken/data/models/response/steward_sales_info_dashboard/steward_sales_info_dashboard.dart';
import 'package:rasadyar_chicken/data/models/response/user_profile/user_profile.dart';
import 'package:rasadyar_chicken/data/models/response/waiting_arrival/waiting_arrival.dart';
import 'package:rasadyar_chicken/data/repositories/chicken/chicken_repository_imp.dart';
import 'package:rasadyar_core/core.dart';
class MockChickenRemoteDatasource extends Mock
implements ChickenRemoteDatasource {}
class MockChickenLocalDataSource extends Mock
implements ChickenLocalDataSource {}
void main() {
late ChickenRepositoryImp chickenRepository;
late MockChickenRemoteDatasource mockRemote;
late MockChickenLocalDataSource mockLocal;
setUp(() {
mockRemote = MockChickenRemoteDatasource();
mockLocal = MockChickenLocalDataSource();
chickenRepository = ChickenRepositoryImp(
remote: mockRemote,
local: mockLocal,
);
});
group('Steward Workflow Integration Tests', () {
const token = 'test-token';
group('Complete Steward Dashboard Flow', () {
test('should complete full steward dashboard workflow', () async {
// Arrange
const startDate = '2024-01-01';
const endDate = '2024-01-31';
final expectedDashboard = StewardFreeBarDashboard(
totalBars: 1000,
totalQuantity: 800,
totalWeight: 200,
);
final expectedBroadcastPrice = BroadcastPrice(
active: true,
killHousePrice: 45000,
stewardPrice: 50000,
guildPrice: 55000,
);
final expectedProfile = GuildProfile(
key: 'profile-key',
guilds_name: 'Test Guild',
type_activity: 'Test Guild Type',
area_activity: 'Test Guild Type Description',
);
// Mock the flow
when(
() => mockRemote.getStewardDashboard(
token: token,
stratDate: startDate,
endDate: endDate,
),
).thenAnswer((_) async => expectedDashboard);
when(
() => mockRemote.getBroadcastPrice(token: token),
).thenAnswer((_) async => expectedBroadcastPrice);
when(
() => mockRemote.getProfile(token: token),
).thenAnswer((_) async => expectedProfile);
// Act - Step 1: Get steward dashboard
final dashboard = await chickenRepository.getStewardDashboard(
token: token,
stratDate: startDate,
endDate: endDate,
);
// Act - Step 2: Get broadcast price
final broadcastPrice = await chickenRepository.getBroadcastPrice(
token: token,
);
// Act - Step 3: Get profile
final profile = await chickenRepository.getProfile(token: token);
// Assert
expect(dashboard, equals(expectedDashboard));
expect(broadcastPrice, equals(expectedBroadcastPrice));
expect(profile, equals(expectedProfile));
verify(
() => mockRemote.getStewardDashboard(
token: token,
stratDate: startDate,
endDate: endDate,
),
).called(1);
verify(() => mockRemote.getBroadcastPrice(token: token)).called(1);
verify(() => mockRemote.getProfile(token: token)).called(1);
});
});
group('Inventory Management Flow', () {
test('should complete inventory management workflow', () async {
// Arrange
final expectedInventory = [
InventoryModel(
key: 'inventory-1',
name: 'Product 1',
totalCarcassesQuantity: 100,
),
InventoryModel(
key: 'inventory-2',
name: 'Product 2',
totalCarcassesQuantity: 200,
),
];
final expectedKillHouseInfo = KillHouseDistributionInfo(
stewardAllocationsWeight: 1000.0,
freeSalesWeight: 500.0,
);
// Mock the flow
when(
() => mockRemote.getInventory(
token: token,
cancelToken: any(named: 'cancelToken'),
),
).thenAnswer((_) async => expectedInventory);
when(
() => mockRemote.getKillHouseDistributionInfo(token: token),
).thenAnswer((_) async => expectedKillHouseInfo);
// Act - Step 1: Get inventory
final inventory = await chickenRepository.getInventory(token: token);
// Act - Step 2: Get kill house distribution info
final killHouseInfo = await chickenRepository
.getKillHouseDistributionInfo(token: token);
// Assert
expect(inventory, equals(expectedInventory));
expect(killHouseInfo, equals(expectedKillHouseInfo));
verify(
() => mockRemote.getInventory(
token: token,
cancelToken: any(named: 'cancelToken'),
),
).called(1);
verify(
() => mockRemote.getKillHouseDistributionInfo(token: token),
).called(1);
});
});
group('Allocation Management Flow', () {
test('should complete allocation management workflow', () async {
// Arrange
final queryParameters = {'page': '1', 'limit': '10'};
final expectedAllocations = [
AllocatedMadeModel(
key: 'allocation-1',
productName: 'Product 1',
numberOfCarcasses: 100,
),
];
final expectedPagination = PaginationModel<AllocatedMadeModel>(
results: expectedAllocations,
count: 1,
);
final allocationRequest = {
'allocationId': 'allocation-1',
'confirmed': true,
};
// Mock the flow
when(
() => mockRemote.getAllocatedMade(
token: token,
queryParameters: queryParameters,
),
).thenAnswer((_) async => expectedPagination);
when(
() => mockRemote.confirmAllocation(
token: token,
allocation: allocationRequest,
),
).thenAnswer((_) async {});
// Act - Step 1: Get allocated made
final allocations = await chickenRepository.getAllocatedMade(
token: token,
queryParameters: queryParameters,
);
// Act - Step 2: Confirm allocation
await chickenRepository.confirmAllocation(
token: token,
allocation: allocationRequest,
);
// Assert
expect(allocations, equals(expectedPagination));
verify(
() => mockRemote.getAllocatedMade(
token: token,
queryParameters: queryParameters,
),
).called(1);
verify(
() => mockRemote.confirmAllocation(
token: token,
allocation: allocationRequest,
),
).called(1);
});
});
group('Steward Free Bar Management Flow', () {
test('should complete steward free bar management workflow', () async {
// Arrange
final queryParameters = {'page': '1', 'limit': '10'};
final expectedFreeBars = [
StewardFreeBar(
key: 'freebar-1',
killHouseName: 'Bar 1',
weightOfCarcasses: 500.0,
),
];
final expectedPagination = PaginationModel<StewardFreeBar>(
results: expectedFreeBars,
count: 1,
);
final createRequest = CreateStewardFreeBar(
key: 'new-freebar',
killHouseName: 'New Bar',
weightOfCarcasses: 300,
);
// Mock the flow
when(
() => mockRemote.getStewardPurchasesOutSideOfTheProvince(
token: token,
queryParameters: queryParameters,
),
).thenAnswer((_) async => expectedPagination);
when(
() => mockRemote.createStewardPurchasesOutSideOfTheProvince(
token: token,
body: createRequest,
),
).thenAnswer((_) async {});
// Act - Step 1: Get steward purchases outside province
final freeBars = await chickenRepository
.getStewardPurchasesOutSideOfTheProvince(
token: token,
queryParameters: queryParameters,
);
// Act - Step 2: Create new steward purchase
await chickenRepository.createStewardPurchasesOutSideOfTheProvince(
token: token,
body: createRequest,
);
// Assert
expect(freeBars, equals(expectedPagination));
verify(
() => mockRemote.getStewardPurchasesOutSideOfTheProvince(
token: token,
queryParameters: queryParameters,
),
).called(1);
verify(
() => mockRemote.createStewardPurchasesOutSideOfTheProvince(
token: token,
body: createRequest,
),
).called(1);
});
});
group('Segmentation Management Flow', () {
test('should complete segmentation management workflow', () async {
// Arrange
final queryParameters = {'page': '1', 'limit': '10'};
final expectedSegments = [
SegmentationModel(
key: 'segment-1',
result: 'Segment 1',
quota: 'Description 1',
),
];
final expectedPagination = PaginationModel<SegmentationModel>(
results: expectedSegments,
count: 1,
);
final newSegment = SegmentationModel(
key: 'new-segment',
result: 'New Segment',
quota: 'New Description',
);
// Mock the flow
when(
() => mockRemote.getSegmentation(
token: token,
queryParameters: queryParameters,
),
).thenAnswer((_) async => expectedPagination);
when(
() => mockRemote.createSegmentation(token: token, model: newSegment),
).thenAnswer((_) async {});
// Act - Step 1: Get segmentation
final segments = await chickenRepository.getSegmentation(
token: token,
queryParameters: queryParameters,
);
// Act - Step 2: Create new segmentation
await chickenRepository.createSegmentation(
token: token,
model: newSegment,
);
// Assert
expect(segments, equals(expectedPagination));
verify(
() => mockRemote.getSegmentation(
token: token,
queryParameters: queryParameters,
),
).called(1);
verify(
() => mockRemote.createSegmentation(token: token, model: newSegment),
).called(1);
});
});
group('Local Data Integration', () {
test('should integrate local data with remote operations', () async {
// Arrange
final expectedWidelyUsed = WidelyUsedLocalModel(
hasInit: true,
items: [],
);
// Mock local data
when(() => mockLocal.getAllWidely()).thenReturn(expectedWidelyUsed);
// Act
final widelyUsed = chickenRepository.getAllWidely();
// Assert
expect(widelyUsed, equals(expectedWidelyUsed));
verify(() => mockLocal.getAllWidely()).called(1);
});
test('should initialize widely used data', () async {
// Arrange
when(() => mockLocal.initWidleyUsed()).thenAnswer((_) async {});
// Act
await chickenRepository.initWidleyUsed();
// Assert
verify(() => mockLocal.initWidleyUsed()).called(1);
});
});
group('Error Handling in Steward Workflow', () {
test('should handle inventory retrieval failure', () async {
// Arrange
when(
() => mockRemote.getInventory(
token: token,
cancelToken: any(named: 'cancelToken'),
),
).thenAnswer((_) async => null);
// Act
final inventory = await chickenRepository.getInventory(token: token);
// Assert
expect(inventory, isNull);
verify(
() => mockRemote.getInventory(
token: token,
cancelToken: any(named: 'cancelToken'),
),
).called(1);
});
test('should handle allocation confirmation failure', () async {
// Arrange
final allocationRequest = {
'allocationId': 'allocation-1',
'confirmed': true,
};
when(
() => mockRemote.confirmAllocation(
token: token,
allocation: allocationRequest,
),
).thenThrow(Exception('Allocation confirmation failed'));
// Act & Assert
expect(
() => chickenRepository.confirmAllocation(
token: token,
allocation: allocationRequest,
),
throwsA(isA<Exception>()),
);
verify(
() => mockRemote.confirmAllocation(
token: token,
allocation: allocationRequest,
),
).called(1);
});
});
});
}

View File

@@ -0,0 +1,221 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:rasadyar_chicken/presentation/utils/string_utils.dart';
void main() {
group('XStringUtils', () {
group('faAllocationType', () {
test('should convert simple string using utilsMap', () {
// Arrange
const input = 'killhouse';
const expected = 'کشتارگاه به';
// Act
final result = input.faAllocationType;
// Assert
expect(result, equals(expected));
});
test('should convert string with underscore using utilsMap', () {
// Arrange
const input = 'steward_exclusive';
const expected = 'مباشر به اختصاصی';
// Act
final result = input.faAllocationType;
// Assert
expect(result, equals(expected));
});
test('should return original string when not found in utilsMap', () {
// Arrange
const input = 'unknown_string';
const expected = 'unknown به string';
// Act
final result = input.faAllocationType;
// Assert
expect(result, equals(expected));
});
test('should handle single word without underscore', () {
// Arrange
const input = 'free';
const expected = 'آزاد به';
// Act
final result = input.faAllocationType;
// Assert
expect(result, equals(expected));
});
test('should handle multiple underscores correctly', () {
// Arrange
const input = 'steward_exclusive_guild';
const expected = 'مباشر به اختصاصی صنف';
// Act
final result = input.faAllocationType;
// Assert
expect(result, equals(expected));
});
});
group('faItem', () {
test('should convert string using utilsMap', () {
// Arrange
const input = 'pending';
const expected = 'در انتظار';
// Act
final result = input.faItem;
// Assert
expect(result, equals(expected));
});
test('should return original string when not found in utilsMap', () {
// Arrange
const input = 'unknown_item';
const expected = 'unknown_item';
// Act
final result = input.faItem;
// Assert
expect(result, equals(expected));
});
test('should handle empty string', () {
// Arrange
const input = '';
const expected = '';
// Act
final result = input.faItem;
// Assert
expect(result, equals(expected));
});
});
group('buyerIsGuild', () {
test('should return true when last part is guild', () {
// Arrange
const input = 'steward_exclusive_guild';
// Act
final result = input.buyerIsGuild;
// Assert
expect(result, isTrue);
});
test('should return false when last part is not guild', () {
// Arrange
const input = 'steward_exclusive_governmental';
// Act
final result = input.buyerIsGuild;
// Assert
expect(result, isFalse);
});
test('should return false for single word', () {
// Arrange
const input = 'guild';
// Act
final result = input.buyerIsGuild;
// Assert
expect(result, isFalse);
});
test('should return false for empty string', () {
// Arrange
const input = '';
// Act
final result = input.buyerIsGuild;
// Assert
expect(result, isFalse);
});
});
group('faTitle', () {
test('should convert string using utilsMap', () {
// Arrange
const input = 'accepted';
const expected = 'تایید شده';
// Act
final result = input.faTitle;
// Assert
expect(result, equals(expected));
});
test('should return original string when not found in utilsMap', () {
// Arrange
const input = 'unknown_title';
const expected = 'unknown_title';
// Act
final result = input.faTitle;
// Assert
expect(result, equals(expected));
});
test('should handle empty string', () {
// Arrange
const input = '';
const expected = '';
// Act
final result = input.faTitle;
// Assert
expect(result, equals(expected));
});
});
});
group('utilsMap', () {
test('should contain expected key-value pairs', () {
// Assert
expect(utilsMap['killhouse'], equals('کشتارگاه'));
expect(utilsMap['_'], equals('به'));
expect(utilsMap['steward'], equals('مباشر'));
expect(utilsMap['exclusive'], equals('اختصاصی'));
expect(utilsMap['free'], equals('آزاد'));
expect(utilsMap['pending'], equals('در انتظار'));
expect(utilsMap['accepted'], equals('تایید شده'));
expect(utilsMap['guild'], equals('صنف'));
expect(utilsMap['governmental'], equals('دولتی'));
});
test('should not be empty', () {
// Assert
expect(utilsMap.isNotEmpty, isTrue);
});
test('should have consistent key-value pairs', () {
// Assert
expect(utilsMap.length, equals(9));
expect(utilsMap.keys, contains('killhouse'));
expect(utilsMap.keys, contains('steward'));
expect(utilsMap.keys, contains('guild'));
expect(utilsMap.values, contains('کشتارگاه'));
expect(utilsMap.values, contains('مباشر'));
expect(utilsMap.values, contains('صنف'));
});
});
}

View File

@@ -0,0 +1,11 @@
import 'package:flutter_test/flutter_test.dart';
void main() {
group('Chicken Package Test Suite', () {
test('Test suite placeholder', () {
// This file serves as a test runner for the entire chicken package
// Individual test files should be run separately
expect(true, isTrue);
});
});
}

View File

@@ -0,0 +1,32 @@
import 'package:flutter_test/flutter_test.dart';
/// Test configuration for the chicken package
class TestConfig {
static const String testToken = 'test-token-12345';
static const String testPhoneNumber = '09123456789';
static const String testDeviceName = 'Test Device';
static const Map<String, dynamic> testAuthRequest = {
'username': 'test@example.com',
'password': 'testpassword123',
};
static const Map<String, dynamic> testQueryParameters = {
'page': '1',
'limit': '10',
};
static const String testStartDate = '2024-01-01';
static const String testEndDate = '2024-01-31';
/// Setup method for test initialization
static void setupTests() {
// Configure test environment
TestWidgetsFlutterBinding.ensureInitialized();
}
/// Cleanup method for test teardown
static void cleanupTests() {
// Clean up test resources
}
}