test: add unit tests for poultry repository and searchable dropdown functionalities

- Introduced tests for `PoultryScienceRepositoryImp` to validate delegated remote calls.
- Added comprehensive tests for `SearchableDropdownLogic` covering selection, overlay, and search logic.
- Enhanced `SearchableDropdown` widget tests for multi-select, label building, and overlay management.
This commit is contained in:
2025-11-16 15:40:21 +03:30
parent 716a7ed259
commit a66c8b69ca
16 changed files with 812 additions and 304 deletions

View File

@@ -58,7 +58,8 @@ class _MonthlyDataCalendarState extends State<MonthlyDataCalendar> {
@override
void didUpdateWidget(MonthlyDataCalendar oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.selectedDate != widget.selectedDate || oldWidget.dayData != widget.dayData) {
if (oldWidget.selectedDate != widget.selectedDate ||
oldWidget.dayData != widget.dayData) {
_generateCalendar();
_updateDisplayValue();
}
@@ -79,25 +80,31 @@ class _MonthlyDataCalendarState extends State<MonthlyDataCalendar> {
final twoDaysAgoStr = twoDaysAgo.formatCompactDate();
final oneDayAgoStr = oneDayAgo.formatCompactDate();
return dateStr == todayStr || dateStr == twoDaysAgoStr || dateStr == oneDayAgoStr;
return dateStr == todayStr ||
dateStr == twoDaysAgoStr ||
dateStr == oneDayAgoStr;
}
void _generateCalendar() {
final days = _computeCalendarDays();
setState(() {
_calendarDays = days;
});
}
List<DayInfo?> _computeCalendarDays() {
final days = <DayInfo?>[];
final year = _currentMonth.year;
final month = _currentMonth.month;
final daysInMonth = _currentMonth.monthLength;
// Get first day of month to determine starting position
final firstDayOfMonth = Jalali(year, month, 1);
final dayOfWeek = firstDayOfMonth.weekDay; // 1 = Saturday in shamsi_date
final dayOfWeek = firstDayOfMonth.weekDay;
// Add empty cells for days before the first day of month
for (int i = 1; i < dayOfWeek; i++) {
days.add(null);
}
// Add all days of the month
for (int day = 1; day <= daysInMonth; day++) {
final date = Jalali(year, month, day);
final today = Jalali.now();
@@ -111,21 +118,23 @@ class _MonthlyDataCalendarState extends State<MonthlyDataCalendar> {
date: date,
day: day,
formattedDate: formattedDate,
isToday: date.year == today.year && date.month == today.month && date.day == today.day,
isToday:
date.year == today.year &&
date.month == today.month &&
date.day == today.day,
isEnabled: isEnabled,
hasZeroValue: hasZeroValue,
remainingStock: data?.value ?? 0,
),
);
}
setState(() {
_calendarDays = days;
});
return days;
}
void _handleDayClick(DayInfo dayInfo) {
if (dayInfo.isEnabled && !dayInfo.hasZeroValue && widget.onDateSelect != null) {
if (dayInfo.isEnabled &&
!dayInfo.hasZeroValue &&
widget.onDateSelect != null) {
widget.onDateSelect!(dayInfo);
Navigator.pop(context);
}
@@ -176,7 +185,8 @@ class _MonthlyDataCalendarState extends State<MonthlyDataCalendar> {
if (dayInfo != null) {
final persianDay = _toPersianNumber(dayInfo.day);
_textController.text = '$persianDay ${_monthNames[dayInfo.date.month - 1]}';
_textController.text =
'$persianDay ${_monthNames[dayInfo.date.month - 1]}';
} else {
_textController.text = widget.selectedDate ?? '';
}
@@ -198,23 +208,31 @@ class _MonthlyDataCalendarState extends State<MonthlyDataCalendar> {
context: context,
builder: (BuildContext context) {
return Dialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(24)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(24),
),
child: Container(
constraints: const BoxConstraints(maxWidth: 650, maxHeight: 650),
child: Card(
elevation: 3,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(24)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(24),
),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
_buildHeader(),
const SizedBox(height: 16),
_buildDayNamesHeader(),
const SizedBox(height: 8),
_buildCalendarGrid(),
],
child: StatefulBuilder(
builder: (context, setStateDialog) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
_buildHeaderWithDialogSet(setStateDialog),
const SizedBox(height: 16),
_buildDayNamesHeader(),
const SizedBox(height: 8),
_buildCalendarGrid(),
],
);
},
),
),
),
@@ -224,6 +242,64 @@ class _MonthlyDataCalendarState extends State<MonthlyDataCalendar> {
);
}
Widget _buildHeaderWithDialogSet(
void Function(void Function()) setStateDialog,
) {
return Container(
padding: const EdgeInsets.only(bottom: 16),
decoration: const BoxDecoration(
border: Border(bottom: BorderSide(color: Color(0xFFF0F0F0), width: 2)),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
IconButton(
onPressed: () {
setStateDialog(() {
if (_currentMonth.month == 1) {
_currentMonth = Jalali(_currentMonth.year - 1, 12, 1);
} else {
_currentMonth = Jalali(
_currentMonth.year,
_currentMonth.month - 1,
1,
);
}
_calendarDays = _computeCalendarDays();
});
},
icon: const Icon(Icons.chevron_left),
),
Text(
'${_monthNames[_currentMonth.month - 1]} ${_toPersianNumber(_currentMonth.year)}',
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
color: Color(0xFF333333),
),
),
IconButton(
onPressed: () {
setStateDialog(() {
if (_currentMonth.month == 12) {
_currentMonth = Jalali(_currentMonth.year + 1, 1, 1);
} else {
_currentMonth = Jalali(
_currentMonth.year,
_currentMonth.month + 1,
1,
);
}
_calendarDays = _computeCalendarDays();
});
},
icon: const Icon(Icons.chevron_right),
),
],
),
);
}
Widget _buildHeader() {
return Container(
padding: const EdgeInsets.only(bottom: 16),
@@ -233,7 +309,10 @@ class _MonthlyDataCalendarState extends State<MonthlyDataCalendar> {
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
IconButton(onPressed: _handlePrevMonth, icon: const Icon(Icons.chevron_right)),
IconButton(
onPressed: _handlePrevMonth,
icon: const Icon(Icons.chevron_left),
),
Text(
'${_monthNames[_currentMonth.month - 1]} ${_toPersianNumber(_currentMonth.year)}',
style: const TextStyle(
@@ -242,7 +321,10 @@ class _MonthlyDataCalendarState extends State<MonthlyDataCalendar> {
color: Color(0xFF333333),
),
),
IconButton(onPressed: _handleNextMonth, icon: const Icon(Icons.chevron_left)),
IconButton(
onPressed: _handleNextMonth,
icon: const Icon(Icons.chevron_right),
),
],
),
);
@@ -333,7 +415,9 @@ class _MonthlyDataCalendarState extends State<MonthlyDataCalendar> {
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14,
color: dayInfo.isToday ? const Color(0xFFFF9800) : const Color(0xFF333333),
color: dayInfo.isToday
? const Color(0xFFFF9800)
: const Color(0xFF333333),
),
),
if (data != null && data.value != null) ...[

View File

@@ -96,7 +96,7 @@ class SearchableDropdownLogic<T> extends GetxController {
if (selectedItem != null) {
this.selectedItem.value = selectedItem;
} else {
this.selectedItem.value = initialValue != null ? [?initialValue] : [];
this.selectedItem.value = initialValue != null ? [initialValue as T] : [];
}
}