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:
Binary file not shown.
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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) {
|
||||
|
||||
130
packages/chicken/test/README.md
Normal file
130
packages/chicken/test/README.md
Normal 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
|
||||
@@ -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'));
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -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>()),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -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', () {
|
||||
|
||||
@@ -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'));
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -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 {}
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
221
packages/chicken/test/presentation/utils/string_utils_test.dart
Normal file
221
packages/chicken/test/presentation/utils/string_utils_test.dart
Normal 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('صنف'));
|
||||
});
|
||||
});
|
||||
}
|
||||
11
packages/chicken/test/run_tests.dart
Normal file
11
packages/chicken/test/run_tests.dart
Normal 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);
|
||||
});
|
||||
});
|
||||
}
|
||||
32
packages/chicken/test/test_config.dart
Normal file
32
packages/chicken/test/test_config.dart
Normal 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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user