some fixes

This commit is contained in:
amirrezaghabeli
2025-10-06 10:32:58 +03:30
parent 0653cdb6d7
commit 7e1240dffb
20 changed files with 40 additions and 2012 deletions
+18 -8
View File
@@ -2,6 +2,7 @@ import 'dart:io';
import 'package:dio/dio.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:tm_app/data/services/model/error/error_model.dart';
import 'package:tm_app/data/services/model/login_response/login_response_model.dart';
import '../config/app_config.dart';
@@ -305,15 +306,24 @@ class NetworkManager {
return 'Unknown error';
}
if (data is Map<String, dynamic>) {
return data['message'] ??
data['error'] ??
data['detail'] ??
'Unknown error';
}
try {
if (data is Map<String, dynamic>) {
final Object? nested = data['error'];
final Map<String, dynamic> json =
nested is Map<String, dynamic> ? nested : data;
final ErrorModel model = ErrorModel.fromJson(json);
return model.message ??
model.details ??
model.code ??
(data['detail'] as String?) ??
'Unknown error';
}
if (data is String) {
return data;
if (data is String) {
return data;
}
} catch (_) {
// fall through to default message
}
return 'Unknown error';
+19
View File
@@ -0,0 +1,19 @@
import 'package:tm_app/core/network/app_exceptions.dart';
/// Utility class for handling error messages
class ErrorUtils {
/// Extracts a clean error message from an Exception
static String getErrorMessage(Exception error) {
if (error is AppException) {
return error.message;
}
// For plain Exception, remove "Exception: " prefix
final errorString = error.toString();
if (errorString.startsWith('Exception: ')) {
return errorString.substring(11);
}
return errorString;
}
}
+3 -2
View File
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:tm_app/core/theme/colors.dart';
import 'package:tm_app/core/utils/error_utils.dart';
import 'package:tm_app/core/utils/result.dart';
import 'package:tm_app/data/repositories/your_tenders_repository.dart';
import 'package:tm_app/data/services/model/liked_tenders_response/liked_tenders_response.dart';
@@ -109,7 +110,7 @@ class YourTendersViewModel with ChangeNotifier {
errorMessage = null;
break;
case Error<TenderApprovalsResponse>():
errorMessage = result.error.toString();
errorMessage = ErrorUtils.getErrorMessage(result.error);
break;
}
@@ -175,7 +176,7 @@ class YourTendersViewModel with ChangeNotifier {
errorMessage = null;
break;
case Error<LikedTendersResponse>():
errorMessage = result.error.toString();
errorMessage = ErrorUtils.getErrorMessage(result.error);
break;
}
-283
View File
@@ -1,283 +0,0 @@
import 'package:tm_app/data/services/model/customer/customer.dart';
import 'package:tm_app/data/services/model/forgot_password_response/forgot_password_response_model.dart';
import 'package:tm_app/data/services/model/liked_tender_feedback/liked_tender_feedback.dart';
import 'package:tm_app/data/services/model/liked_tenders_data/liked_tenders_data.dart';
import 'package:tm_app/data/services/model/liked_tenders_response/liked_tenders_response.dart';
import 'package:tm_app/data/services/model/login_data/login_data.dart';
import 'package:tm_app/data/services/model/login_response/login_response_model.dart';
import 'package:tm_app/data/services/model/logout_response/logout_response.dart';
import 'package:tm_app/data/services/model/meta/meta.dart';
import 'package:tm_app/data/services/model/reset_password_response/reset_password_response.dart';
import 'package:tm_app/data/services/model/tender_approvals_data/tender_approvals_data.dart';
import 'package:tm_app/data/services/model/tender_approvals_data_warpper/tender_approvals_data_wrapper.dart';
import 'package:tm_app/data/services/model/tender_approvals_response/tender_approvals_response.dart';
import 'package:tm_app/data/services/model/tender_data/tender_data.dart';
import 'package:tm_app/data/services/model/verify_otp_data/verify_otp_data.dart';
import 'package:tm_app/data/services/model/verify_otp_response/verify_otp_response_model.dart';
class TestFixtures {
static const customer = Customer(
id: '1',
type: 'individual',
status: 'active',
firstName: 'John',
lastName: 'Doe',
fullName: 'John Doe',
email: 'john.doe@example.com',
phone: '+1234567890',
mobile: '+1234567890',
registrationNumber: 'REG123',
taxId: 'TAX123',
industry: 'Technology',
businessType: 'Software',
employeeCount: 10,
annualRevenue: 1000000,
foundedYear: 2020,
isVerified: true,
isCompliant: true,
complianceNotes: 'Compliant',
language: 'en',
username: 'johndoe',
currency: 'USD',
timezone: 'UTC',
createdAt: 1640995200,
updatedAt: 1640995200,
createdBy: 'admin',
updatedBy: 'admin',
address: null,
contactPersons: null,
);
static const loginData = LoginData(
customer: customer,
error: null,
accessToken: 'test_token',
refreshToken: 'test_refresh_token',
expiresAt: 1640995200,
);
static const loginResponse = LoginResponseModel(
success: true,
message: 'Login successful',
data: loginData,
);
static const logoutResponse = LogoutResponse(
success: true,
message: 'Logout successful',
error: null,
);
static const forgotPasswordResponse = ForgotPasswordResponseModel(
success: true,
message: 'OTP sent to email',
data: null,
error: null,
meta: null,
);
static const verifyOtpData = VerifyOtpData(
message: 'OTP verified successfully',
success: true,
token: 'reset_token_123',
);
static const verifyOtpResponse = VerifyOtpResponseModel(
success: true,
message: 'OTP verified successfully',
data: verifyOtpData,
);
static const resetPasswordResponse = ResetPasswordResponse(
success: true,
message: 'Password reset successfully',
error: null,
);
// Tender test fixtures
static const testTenderData = TenderData(
success: true,
message: 'Success',
id: '1',
tenderId: '1',
noticePublicationId: '1',
publicationDate: 1640995200,
title: 'Test Tender',
description: 'Test Description',
procurementTypeCode: 'test',
procedureCode: 'test',
estimatedValue: 100000,
currency: 'USD',
duration: '30',
tenderDeadline: 1640995200,
submissionDeadline: 1640995200,
applicationDeadline: 1640995200,
submissionUrl: 'https://test.com',
countryCode: 'US',
durationUnit: 'days',
buyerOrganization: null,
status: 'submitted',
);
static const testTenderApprovalsData = TenderApprovalsData(
id: '1',
status: 'submitted',
tenderId: '1',
companyId: '1',
submissionMode: 'online',
createdAt: 1640995200,
tender: testTenderData,
);
static const testMeta = Meta(
limit: 10,
offset: 0,
page: 1,
pages: 2,
total: 15,
);
static const testTenderApprovalsDataWrapper = TenderApprovalsDataWrapper(
tenders: [testTenderApprovalsData],
metadata: testMeta,
);
static const tenderApprovalsResponse = TenderApprovalsResponse(
success: true,
error: null,
data: testTenderApprovalsDataWrapper,
);
static const testLikedTenderFeedback = LikedTenderFeedback(
id: '1',
feedbackType: 'liked',
updatedAt: 1640995200,
createdAt: 1640995200,
tenderId: '1',
customerId: '1',
companyId: '1',
tender: testTenderData,
);
static const testLikedTenderFeedback2 = LikedTenderFeedback(
id: '2',
feedbackType: 'liked',
updatedAt: 1640995200,
createdAt: 1640995200,
tenderId: '2',
customerId: '1',
companyId: '1',
tender: testTenderData,
);
static const testLikedTenderFeedback3 = LikedTenderFeedback(
id: '3',
feedbackType: 'liked',
updatedAt: 1640995200,
createdAt: 1640995200,
tenderId: '3',
customerId: '1',
companyId: '1',
tender: testTenderData,
);
static const testLikedTenderFeedback4 = LikedTenderFeedback(
id: '4',
feedbackType: 'liked',
updatedAt: 1640995200,
createdAt: 1640995200,
tenderId: '4',
customerId: '1',
companyId: '1',
tender: testTenderData,
);
static const testLikedTenderFeedback5 = LikedTenderFeedback(
id: '5',
feedbackType: 'liked',
updatedAt: 1640995200,
createdAt: 1640995200,
tenderId: '5',
customerId: '1',
companyId: '1',
tender: testTenderData,
);
static const testLikedTenderFeedback6 = LikedTenderFeedback(
id: '6',
feedbackType: 'liked',
updatedAt: 1640995200,
createdAt: 1640995200,
tenderId: '6',
customerId: '1',
companyId: '1',
tender: testTenderData,
);
static const testLikedTenderFeedback7 = LikedTenderFeedback(
id: '7',
feedbackType: 'liked',
updatedAt: 1640995200,
createdAt: 1640995200,
tenderId: '7',
customerId: '1',
companyId: '1',
tender: testTenderData,
);
static const testLikedTenderFeedback8 = LikedTenderFeedback(
id: '8',
feedbackType: 'liked',
updatedAt: 1640995200,
createdAt: 1640995200,
tenderId: '8',
customerId: '1',
companyId: '1',
tender: testTenderData,
);
static const testLikedTenderFeedback9 = LikedTenderFeedback(
id: '9',
feedbackType: 'liked',
updatedAt: 1640995200,
createdAt: 1640995200,
tenderId: '9',
customerId: '1',
companyId: '1',
tender: testTenderData,
);
static const testLikedTenderFeedback10 = LikedTenderFeedback(
id: '10',
feedbackType: 'liked',
updatedAt: 1640995200,
createdAt: 1640995200,
tenderId: '10',
customerId: '1',
companyId: '1',
tender: testTenderData,
);
static const testLikedTendersData = LikedTendersData(
feedback: [
testLikedTenderFeedback,
testLikedTenderFeedback2,
testLikedTenderFeedback3,
testLikedTenderFeedback4,
testLikedTenderFeedback5,
testLikedTenderFeedback6,
testLikedTenderFeedback7,
testLikedTenderFeedback8,
testLikedTenderFeedback9,
testLikedTenderFeedback10,
],
meta: testMeta,
);
static const likedTendersResponse = LikedTendersResponse(
success: true,
message: 'Success',
data: testLikedTendersData,
error: null,
);
}
-97
View File
@@ -1,97 +0,0 @@
import 'package:tm_app/core/utils/result.dart';
import 'package:tm_app/data/repositories/auth_repository.dart';
import 'package:tm_app/data/services/model/forgot_password_response/forgot_password_response_model.dart';
import 'package:tm_app/data/services/model/login_response/login_response_model.dart';
import 'package:tm_app/data/services/model/logout_response/logout_response.dart';
import 'package:tm_app/data/services/model/reset_password_response/reset_password_response.dart';
import 'package:tm_app/data/services/model/verify_otp_response/verify_otp_response_model.dart';
import '../fixtures/test_fixtures.dart';
class MockAuthRepository implements AuthRepository {
Result<LoginResponseModel>? _loginResult;
Result<LogoutResponse>? _logoutResult;
Result<ForgotPasswordResponseModel>? _forgotPasswordResult;
Result<VerifyOtpResponseModel>? _verifyOtpResult;
Result<ResetPasswordResponse>? _resetPasswordResult;
Result<bool>? _checkIsLoggedInResult;
Exception? _localLogoutException;
void setLoginResult(Result<LoginResponseModel> result) {
_loginResult = result;
}
void setLogoutResult(Result<LogoutResponse> result) {
_logoutResult = result;
}
void setForgotPasswordResult(Result<ForgotPasswordResponseModel> result) {
_forgotPasswordResult = result;
}
void setVerifyOtpResult(Result<VerifyOtpResponseModel> result) {
_verifyOtpResult = result;
}
void setResetPasswordResult(Result<ResetPasswordResponse> result) {
_resetPasswordResult = result;
}
void setCheckIsLoggedInResult(Result<bool> result) {
_checkIsLoggedInResult = result;
}
void setLocalLogoutException(Exception exception) {
_localLogoutException = exception;
}
@override
Future<Result<LoginResponseModel>> login({
required String username,
required String password,
}) async {
return _loginResult ?? const Result.ok(TestFixtures.loginResponse);
}
@override
Future<Result<LogoutResponse>> logout() async {
return _logoutResult ?? const Result.ok(TestFixtures.logoutResponse);
}
@override
Future<Result<ResetPasswordResponse>> resetPassword({
required String newPassword,
required String token,
}) async {
return _resetPasswordResult ??
const Result.ok(TestFixtures.resetPasswordResponse);
}
@override
Future<void> localLogout() async {
if (_localLogoutException != null) {
throw _localLogoutException!;
}
}
@override
Future<Result<ForgotPasswordResponseModel>> forgotPassword({
required String email,
}) async {
return _forgotPasswordResult ??
const Result.ok(TestFixtures.forgotPasswordResponse);
}
@override
Future<Result<VerifyOtpResponseModel>> verifyOtp({
required String code,
required String email,
}) async {
return _verifyOtpResult ?? const Result.ok(TestFixtures.verifyOtpResponse);
}
@override
Future<Result<bool>> checkIsLoggedIn() async {
return _checkIsLoggedInResult ?? const Result.ok(true);
}
}
@@ -1,37 +0,0 @@
import 'package:tm_app/core/utils/result.dart';
import 'package:tm_app/data/repositories/your_tenders_repository.dart';
import 'package:tm_app/data/services/model/liked_tenders_response/liked_tenders_response.dart';
import 'package:tm_app/data/services/model/request_models/get_tenders_request_model/get_tenders_request_model.dart';
import 'package:tm_app/data/services/model/request_models/tender_feedback_request_model/tender_feedback_request_model.dart';
import 'package:tm_app/data/services/model/tender_approvals_response/tender_approvals_response.dart';
import '../fixtures/test_fixtures.dart';
class MockYourTendersRepository implements YourTendersRepository {
Result<TenderApprovalsResponse>? _getTendersResult;
Result<LikedTendersResponse>? _getTendersFeedbackResult;
void setGetTendersResult(Result<TenderApprovalsResponse> result) {
_getTendersResult = result;
}
void setGetTendersFeedbackResult(Result<LikedTendersResponse> result) {
_getTendersFeedbackResult = result;
}
@override
Future<Result<TenderApprovalsResponse>> getTenders({
required GetTendersRequestModel requestModel,
}) async {
return _getTendersResult ??
const Result.ok(TestFixtures.tenderApprovalsResponse);
}
@override
Future<Result<LikedTendersResponse>> getTendersFeedback({
required TenderFeedbackRequestModel requestModel,
}) async {
return _getTendersFeedbackResult ??
const Result.ok(TestFixtures.likedTendersResponse);
}
}
@@ -1,107 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:tm_app/core/utils/result.dart';
import 'package:tm_app/view_models/auth_view_model.dart';
import '../fixtures/test_fixtures.dart';
import '../mocks/mock_auth_repository.dart';
void main() {
group('AuthViewModel Forgot Password', () {
late MockAuthRepository mockRepository;
late AuthViewModel authViewModel;
late BuildContext mockContext;
setUp(() {
mockRepository = MockAuthRepository();
authViewModel = AuthViewModel(authRepository: mockRepository);
mockContext = const TestWidget().createElement();
});
tearDown(() {
authViewModel.dispose();
});
test('should send forgot password successfully', () async {
mockRepository.setForgotPasswordResult(
const Result.ok(TestFixtures.forgotPasswordResponse),
);
authViewModel.emailController.text = 'test@example.com';
await authViewModel.forgotPassword(mockContext);
expect(authViewModel.isLoadingForgot, false);
expect(authViewModel.successMessage, 'OTP sent to email');
expect(authViewModel.errorMessageForget, null);
});
test('should handle forgot password error', () async {
const errorMessage = 'Email not found';
mockRepository.setForgotPasswordResult(
Result.error(Exception(errorMessage)),
);
authViewModel.emailController.text = 'nonexistent@example.com';
await authViewModel.forgotPassword(mockContext);
// Wait for the microtask to clear the error message
await Future.microtask(() {});
expect(authViewModel.isLoadingForgot, false);
expect(authViewModel.errorMessageForget, null); // Cleared after notify
expect(authViewModel.successMessage, null);
});
test('should set loading state during forgot password', () async {
mockRepository.setForgotPasswordResult(
const Result.ok(TestFixtures.forgotPasswordResponse),
);
authViewModel.emailController.text = 'test@example.com';
final forgotPasswordFuture = authViewModel.forgotPassword(mockContext);
// Check loading state is set
expect(authViewModel.isLoadingForgot, true);
expect(authViewModel.errorMessageForget, null);
expect(authViewModel.successMessage, null);
await forgotPasswordFuture;
expect(authViewModel.isLoadingForgot, false);
});
test('should clear previous messages before forgot password', () async {
mockRepository.setForgotPasswordResult(
const Result.ok(TestFixtures.forgotPasswordResponse),
);
// Set previous error and success messages
authViewModel.emailController.text = 'test@example.com';
mockRepository.setForgotPasswordResult(
Result.error(Exception('Previous error')),
);
await authViewModel.forgotPassword(mockContext);
// Now test successful case
mockRepository.setForgotPasswordResult(
const Result.ok(TestFixtures.forgotPasswordResponse),
);
await authViewModel.forgotPassword(mockContext);
expect(authViewModel.errorMessageForget, null);
expect(authViewModel.successMessage, 'OTP sent to email');
});
});
}
class TestWidget extends StatelessWidget {
const TestWidget({super.key});
@override
Widget build(BuildContext context) {
return Container();
}
}
@@ -1,62 +0,0 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:tm_app/view_models/auth_view_model.dart';
import '../mocks/mock_auth_repository.dart';
void main() {
group('AuthViewModel Initialization', () {
late MockAuthRepository mockRepository;
late AuthViewModel authViewModel;
setUp(() {
mockRepository = MockAuthRepository();
authViewModel = AuthViewModel(authRepository: mockRepository);
});
tearDown(() {
authViewModel.dispose();
});
test('should initialize with default values', () {
expect(authViewModel.isLoading, false);
expect(authViewModel.isLoggedIn, false);
expect(authViewModel.isLoadingForgot, false);
expect(authViewModel.isLoadingOtp, false);
expect(authViewModel.isResetPasswordLoading, false);
expect(authViewModel.errorMessage, null);
expect(authViewModel.errorMessageForget, null);
expect(authViewModel.resetPasswordErrorMessage, null);
expect(authViewModel.errorMessageOtp, null);
expect(authViewModel.successMessageOtp, null);
expect(authViewModel.verifyOtpSuccess, false);
expect(authViewModel.loggedInUser, null);
expect(authViewModel.forgotPasswordMessage, null);
expect(authViewModel.resetToken, null);
expect(authViewModel.resetPasswordSuccess, false);
});
test('should initialize controllers with empty text', () {
expect(authViewModel.usernameController.text, isEmpty);
expect(authViewModel.passwordController.text, isEmpty);
expect(authViewModel.emailController.text, isEmpty);
expect(authViewModel.newPasswordController.text, isEmpty);
expect(authViewModel.otpController.text, isEmpty);
});
test('should initialize with password visibility obscured', () {
expect(authViewModel.obscurePassword, true);
expect(authViewModel.obscureForgotPassword, true);
});
test('should not be login enabled with empty fields', () {
expect(authViewModel.isLoginEnabled, false);
});
test('should be login enabled with filled fields', () {
authViewModel.usernameController.text = 'testuser';
authViewModel.passwordController.text = 'testpass';
expect(authViewModel.isLoginEnabled, true);
});
});
}
@@ -1,86 +0,0 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:tm_app/core/utils/result.dart';
import 'package:tm_app/view_models/auth_view_model.dart';
import '../fixtures/test_fixtures.dart';
import '../mocks/mock_auth_repository.dart';
void main() {
group('AuthViewModel Login', () {
late MockAuthRepository mockRepository;
late AuthViewModel authViewModel;
setUp(() {
mockRepository = MockAuthRepository();
authViewModel = AuthViewModel(authRepository: mockRepository);
});
tearDown(() {
authViewModel.dispose();
});
test('should login successfully', () async {
mockRepository.setLoginResult(
const Result.ok(TestFixtures.loginResponse),
);
authViewModel.usernameController.text = 'testuser';
authViewModel.passwordController.text = 'testpass';
await authViewModel.login();
expect(authViewModel.isLoading, false);
expect(authViewModel.loggedInUser, TestFixtures.customer);
expect(authViewModel.usernameController.text, isEmpty);
expect(authViewModel.passwordController.text, isEmpty);
expect(authViewModel.errorMessage, null);
});
test('should handle login error', () async {
const errorMessage = 'Invalid credentials';
mockRepository.setLoginResult(Result.error(Exception(errorMessage)));
authViewModel.usernameController.text = 'testuser';
authViewModel.passwordController.text = 'wrongpass';
await authViewModel.login();
expect(authViewModel.isLoading, false);
expect(authViewModel.loggedInUser, null);
expect(authViewModel.errorMessage, null); // Cleared after notify
});
test('should set loading state during login', () async {
mockRepository.setLoginResult(
const Result.ok(TestFixtures.loginResponse),
);
authViewModel.usernameController.text = 'testuser';
authViewModel.passwordController.text = 'testpass';
final loginFuture = authViewModel.login();
// Check loading state is set
expect(authViewModel.isLoading, true);
expect(authViewModel.errorMessage, null);
await loginFuture;
expect(authViewModel.isLoading, false);
});
test('should call localLogout before login', () async {
mockRepository.setLoginResult(
const Result.ok(TestFixtures.loginResponse),
);
authViewModel.usernameController.text = 'testuser';
authViewModel.passwordController.text = 'testpass';
await authViewModel.login();
// Verify that localLogout was called (user is null initially)
expect(authViewModel.loggedInUser, TestFixtures.customer);
});
});
}
@@ -1,84 +0,0 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:tm_app/core/utils/result.dart';
import 'package:tm_app/view_models/auth_view_model.dart';
import '../fixtures/test_fixtures.dart';
import '../mocks/mock_auth_repository.dart';
void main() {
group('AuthViewModel Logout', () {
late MockAuthRepository mockRepository;
late AuthViewModel authViewModel;
setUp(() {
mockRepository = MockAuthRepository();
authViewModel = AuthViewModel(authRepository: mockRepository);
});
tearDown(() {
authViewModel.dispose();
});
test('should logout successfully', () async {
mockRepository.setLogoutResult(
const Result.ok(TestFixtures.logoutResponse),
);
// Set a logged in user first
authViewModel.usernameController.text = 'testuser';
authViewModel.passwordController.text = 'testpass';
mockRepository.setLoginResult(
const Result.ok(TestFixtures.loginResponse),
);
await authViewModel.login();
await authViewModel.logout();
expect(authViewModel.loggedInUser, null);
expect(authViewModel.errorMessage, null); // Cleared after notify
});
test('should handle logout error', () async {
const errorMessage = 'Logout failed';
mockRepository.setLogoutResult(Result.error(Exception(errorMessage)));
await authViewModel.logout();
expect(authViewModel.errorMessage, null); // Cleared after notify
});
test('should perform local logout', () async {
mockRepository.setLogoutResult(
const Result.ok(TestFixtures.logoutResponse),
);
// Set a logged in user first
authViewModel.usernameController.text = 'testuser';
authViewModel.passwordController.text = 'testpass';
mockRepository.setLoginResult(
const Result.ok(TestFixtures.loginResponse),
);
await authViewModel.login();
await authViewModel.logout();
expect(authViewModel.loggedInUser, null);
});
test('should clear error message before logout', () async {
mockRepository.setLogoutResult(
const Result.ok(TestFixtures.logoutResponse),
);
// Set an error first
authViewModel.usernameController.text = 'testuser';
authViewModel.passwordController.text = 'wrongpass';
mockRepository.setLoginResult(Result.error(Exception('Login failed')));
await authViewModel.login();
await authViewModel.logout();
expect(authViewModel.errorMessage, null);
});
});
}
@@ -1,122 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:tm_app/core/utils/result.dart';
import 'package:tm_app/view_models/auth_view_model.dart';
import '../fixtures/test_fixtures.dart';
import '../mocks/mock_auth_repository.dart';
void main() {
group('AuthViewModel OTP Verification', () {
late MockAuthRepository mockRepository;
late AuthViewModel authViewModel;
late BuildContext mockContext;
setUp(() {
mockRepository = MockAuthRepository();
authViewModel = AuthViewModel(authRepository: mockRepository);
mockContext = const TestWidget().createElement();
});
tearDown(() {
authViewModel.dispose();
});
test('should verify OTP successfully', () async {
mockRepository.setVerifyOtpResult(
const Result.ok(TestFixtures.verifyOtpResponse),
);
authViewModel.otpController.text = '123456';
authViewModel.emailController.text = 'test@example.com';
await authViewModel.verifyOtp(mockContext);
expect(authViewModel.isLoadingOtp, false);
expect(authViewModel.verifyOtpSuccess, true);
expect(authViewModel.resetToken, 'reset_token_123');
expect(
authViewModel.successMessageOtp,
null,
); // Cleared after notify for toast
expect(authViewModel.errorMessageOtp, null); // Cleared after notify
});
test('should handle OTP verification error', () async {
const errorMessage = 'Invalid OTP';
mockRepository.setVerifyOtpResult(Result.error(Exception(errorMessage)));
authViewModel.otpController.text = '000000';
authViewModel.emailController.text = 'test@example.com';
await authViewModel.verifyOtp(mockContext);
expect(authViewModel.isLoadingOtp, false);
expect(authViewModel.verifyOtpSuccess, false);
expect(authViewModel.errorMessageOtp, null); // Cleared after notify
expect(authViewModel.successMessageOtp, null); // Cleared after notify
});
test('should set loading state during OTP verification', () async {
mockRepository.setVerifyOtpResult(
const Result.ok(TestFixtures.verifyOtpResponse),
);
authViewModel.otpController.text = '123456';
authViewModel.emailController.text = 'test@example.com';
final verifyOtpFuture = authViewModel.verifyOtp(mockContext);
// Check loading state is set
expect(authViewModel.isLoadingOtp, true);
expect(authViewModel.errorMessageOtp, null);
expect(authViewModel.verifyOtpSuccess, false);
await verifyOtpFuture;
expect(authViewModel.isLoadingOtp, false);
});
test('should clear OTP flow', () {
// Set some state first
authViewModel.otpController.text = '123456';
authViewModel.resetToken = 'some_token';
authViewModel.clearOtpFlow();
expect(authViewModel.errorMessageOtp, null);
expect(authViewModel.successMessageOtp, null);
expect(authViewModel.isLoadingOtp, false);
expect(authViewModel.verifyOtpSuccess, false);
expect(authViewModel.otpController.text, isEmpty);
expect(authViewModel.resetToken, 'some_token'); // Should not be cleared
});
test('should reset verifyOtpSuccess before verification', () async {
mockRepository.setVerifyOtpResult(
const Result.ok(TestFixtures.verifyOtpResponse),
);
// Set verifyOtpSuccess to true first
authViewModel.otpController.text = '123456';
authViewModel.emailController.text = 'test@example.com';
await authViewModel.verifyOtp(mockContext);
expect(authViewModel.verifyOtpSuccess, true);
// Now test with error
mockRepository.setVerifyOtpResult(Result.error(Exception('Invalid OTP')));
await authViewModel.verifyOtp(mockContext);
expect(authViewModel.verifyOtpSuccess, false);
});
});
}
class TestWidget extends StatelessWidget {
const TestWidget({super.key});
@override
Widget build(BuildContext context) {
return Container();
}
}
@@ -1,140 +0,0 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:tm_app/core/utils/result.dart';
import 'package:tm_app/view_models/auth_view_model.dart';
import '../fixtures/test_fixtures.dart';
import '../mocks/mock_auth_repository.dart';
void main() {
group('AuthViewModel Reset Password', () {
late MockAuthRepository mockRepository;
late AuthViewModel authViewModel;
setUp(() {
mockRepository = MockAuthRepository();
authViewModel = AuthViewModel(authRepository: mockRepository);
});
tearDown(() {
authViewModel.dispose();
});
test('should reset password successfully', () async {
mockRepository.setResetPasswordResult(
const Result.ok(TestFixtures.resetPasswordResponse),
);
authViewModel.resetToken = 'reset_token_123';
authViewModel.newPasswordController.text = 'NewPass123!';
await authViewModel.resetPassword();
expect(authViewModel.isResetPasswordLoading, false);
expect(authViewModel.resetPasswordSuccess, true);
expect(
authViewModel.resetPasswordErrorMessage,
null,
); // Cleared after notify
});
test('should handle reset password error', () async {
const errorMessage = 'Invalid token';
mockRepository.setResetPasswordResult(
Result.error(Exception(errorMessage)),
);
authViewModel.resetToken = 'invalid_token';
authViewModel.newPasswordController.text = 'NewPass123!';
await authViewModel.resetPassword();
expect(authViewModel.isResetPasswordLoading, false);
expect(authViewModel.resetPasswordSuccess, false);
expect(
authViewModel.resetPasswordErrorMessage,
null,
); // Cleared after notify
});
test('should handle missing reset token', () async {
authViewModel.resetToken = null;
authViewModel.newPasswordController.text = 'NewPass123!';
await authViewModel.resetPassword();
expect(authViewModel.isResetPasswordLoading, false);
expect(authViewModel.resetPasswordSuccess, false);
expect(
authViewModel.resetPasswordErrorMessage,
'Reset token is required',
);
});
test('should set loading state during reset password', () async {
mockRepository.setResetPasswordResult(
const Result.ok(TestFixtures.resetPasswordResponse),
);
authViewModel.resetToken = 'reset_token_123';
authViewModel.newPasswordController.text = 'NewPass123!';
final resetPasswordFuture = authViewModel.resetPassword();
// Check loading state is set
expect(authViewModel.isResetPasswordLoading, true);
expect(authViewModel.resetPasswordErrorMessage, null);
expect(authViewModel.resetPasswordSuccess, false);
await resetPasswordFuture;
expect(authViewModel.isResetPasswordLoading, false);
});
test('should clear reset password success', () {
// Set reset password success through a successful reset
mockRepository.setResetPasswordResult(
const Result.ok(TestFixtures.resetPasswordResponse),
);
authViewModel.resetToken = 'reset_token_123';
authViewModel.newPasswordController.text = 'NewPass123!';
authViewModel.clearResetPasswordSuccess();
expect(authViewModel.resetPasswordSuccess, false);
});
test('should clear reset flow', () {
// Set some state first
authViewModel.resetToken = 'some_token';
authViewModel.newPasswordController.text = 'some_password';
authViewModel.clearResetFlow();
expect(authViewModel.resetPasswordErrorMessage, null);
expect(authViewModel.resetPasswordSuccess, false);
expect(authViewModel.isResetPasswordLoading, false);
expect(authViewModel.newPasswordController.text, isEmpty);
expect(authViewModel.resetToken, 'some_token'); // Should not be cleared
});
test('should reset success state before reset password', () async {
mockRepository.setResetPasswordResult(
const Result.ok(TestFixtures.resetPasswordResponse),
);
// Set resetPasswordSuccess to true first
authViewModel.resetToken = 'reset_token_123';
authViewModel.newPasswordController.text = 'NewPass123!';
await authViewModel.resetPassword();
expect(authViewModel.resetPasswordSuccess, true);
// Now test with error
mockRepository.setResetPasswordResult(
Result.error(Exception('Reset failed')),
);
await authViewModel.resetPassword();
expect(authViewModel.resetPasswordSuccess, false);
});
});
}
@@ -1,128 +0,0 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:tm_app/core/utils/result.dart';
import 'package:tm_app/view_models/auth_view_model.dart';
import '../fixtures/test_fixtures.dart';
import '../mocks/mock_auth_repository.dart';
void main() {
group('AuthViewModel Utility Methods', () {
late MockAuthRepository mockRepository;
late AuthViewModel authViewModel;
setUp(() {
mockRepository = MockAuthRepository();
authViewModel = AuthViewModel(authRepository: mockRepository);
});
tearDown(() {
authViewModel.dispose();
});
group('Password Visibility Toggle', () {
test('should toggle password visibility', () {
expect(authViewModel.obscurePassword, true);
authViewModel.togglePasswordVisibility();
expect(authViewModel.obscurePassword, false);
authViewModel.togglePasswordVisibility();
expect(authViewModel.obscurePassword, true);
});
test('should toggle forgot password visibility', () {
expect(authViewModel.obscureForgotPassword, true);
authViewModel.toggleForgotPasswordVisibility();
expect(authViewModel.obscureForgotPassword, false);
authViewModel.toggleForgotPasswordVisibility();
expect(authViewModel.obscureForgotPassword, true);
});
});
group('Message Clearing', () {
test('should clear messages', () {
// Set some messages first through login error
mockRepository.setLoginResult(Result.error(Exception('Login failed')));
authViewModel.usernameController.text = 'testuser';
authViewModel.passwordController.text = 'wrongpass';
authViewModel.clearMessages();
expect(authViewModel.successMessage, null);
expect(authViewModel.errorMessage, null);
});
});
group('Check Login Status', () {
test('should check if user is logged in - true', () async {
mockRepository.setCheckIsLoggedInResult(const Result.ok(true));
await authViewModel.checkIsLoggedIn();
expect(authViewModel.isLoggedIn, true);
});
test('should check if user is logged in - false', () async {
mockRepository.setCheckIsLoggedInResult(const Result.ok(false));
await authViewModel.checkIsLoggedIn();
expect(authViewModel.isLoggedIn, false);
});
test('should handle check login error', () async {
mockRepository.setCheckIsLoggedInResult(
Result.error(Exception('Check failed')),
);
await authViewModel.checkIsLoggedIn();
expect(authViewModel.isLoggedIn, false);
});
});
group('Local Logout', () {
test('should perform local logout', () async {
// Set a logged in user first
authViewModel.usernameController.text = 'testuser';
authViewModel.passwordController.text = 'testpass';
mockRepository.setLoginResult(
const Result.ok(TestFixtures.loginResponse),
);
await authViewModel.login();
await authViewModel.localLogout();
expect(authViewModel.loggedInUser, null);
});
test('should handle local logout exception', () async {
mockRepository.setLocalLogoutException(
Exception('Local logout failed'),
);
// Should not throw exception
await authViewModel.localLogout();
expect(authViewModel.loggedInUser, null);
});
});
group('Text Change Notification', () {
test('should notify listeners on text change', () {
int notificationCount = 0;
authViewModel.addListener(() {
notificationCount++;
});
// Trigger text change
authViewModel.usernameController.text = 'test';
expect(notificationCount, greaterThan(0));
});
});
});
}
@@ -1,94 +0,0 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:tm_app/view_models/auth_view_model.dart';
import '../mocks/mock_auth_repository.dart';
void main() {
group('AuthViewModel Validation', () {
late MockAuthRepository mockRepository;
late AuthViewModel authViewModel;
setUp(() {
mockRepository = MockAuthRepository();
authViewModel = AuthViewModel(authRepository: mockRepository);
});
tearDown(() {
authViewModel.dispose();
});
group('Email Validation', () {
test('should return true for valid email', () {
authViewModel.emailController.text = 'test@example.com';
expect(authViewModel.isEmailValid, true);
});
test('should return false for invalid email', () {
authViewModel.emailController.text = 'invalid-email';
expect(authViewModel.isEmailValid, false);
});
test('should return false for empty email', () {
authViewModel.emailController.text = '';
expect(authViewModel.isEmailValid, false);
});
test('should return false for email without domain', () {
authViewModel.emailController.text = 'test@';
expect(authViewModel.isEmailValid, false);
});
});
group('OTP Validation', () {
test('should return true for valid 6-digit OTP', () {
authViewModel.otpController.text = '123456';
expect(authViewModel.isOtpValid, true);
expect(authViewModel.canSubmitOtpPage, true);
});
test('should return false for invalid OTP length', () {
authViewModel.otpController.text = '12345';
expect(authViewModel.isOtpValid, false);
expect(authViewModel.canSubmitOtpPage, false);
});
test('should return false for empty OTP', () {
authViewModel.otpController.text = '';
expect(authViewModel.isOtpValid, false);
expect(authViewModel.canSubmitOtpPage, false);
});
});
group('Password Validation', () {
test('should return true for valid password', () {
authViewModel.newPasswordController.text = 'Test123!';
expect(authViewModel.isNewPasswordValid, true);
});
test('should return false for password without uppercase', () {
authViewModel.newPasswordController.text = 'test123!';
expect(authViewModel.isNewPasswordValid, false);
});
test('should return false for password without lowercase', () {
authViewModel.newPasswordController.text = 'TEST123!';
expect(authViewModel.isNewPasswordValid, false);
});
test('should return false for password without special character', () {
authViewModel.newPasswordController.text = 'Test123';
expect(authViewModel.isNewPasswordValid, false);
});
test('should return false for password too short', () {
authViewModel.newPasswordController.text = 'Te1!';
expect(authViewModel.isNewPasswordValid, false);
});
test('should return false for empty password', () {
authViewModel.newPasswordController.text = '';
expect(authViewModel.isNewPasswordValid, false);
});
});
});
}
@@ -1,119 +0,0 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:tm_app/core/utils/result.dart';
import 'package:tm_app/view_models/your_tenders_view_model.dart';
import '../fixtures/test_fixtures.dart';
import '../mocks/mock_your_tenders_repository.dart';
void main() {
group('YourTendersViewModel GetTendersFeedback', () {
late MockYourTendersRepository mockRepository;
late YourTendersViewModel viewModel;
setUp(() {
mockRepository = MockYourTendersRepository();
viewModel = YourTendersViewModel(yourTendersRepository: mockRepository);
});
tearDown(() {
viewModel.dispose();
});
test('should get tenders feedback successfully', () async {
mockRepository.setGetTendersFeedbackResult(
const Result.ok(TestFixtures.likedTendersResponse),
);
await viewModel.getTendersFeedback(feedbackType: 'liked', reset: true);
expect(viewModel.isLoading, false);
expect(viewModel.likedTendersData, TestFixtures.likedTendersResponse);
expect(viewModel.errorMessage, null);
expect(viewModel.hasMore, true);
});
test('should handle get tenders feedback error', () async {
const errorMessage = 'Network error';
mockRepository.setGetTendersFeedbackResult(
Result.error(Exception(errorMessage)),
);
await viewModel.getTendersFeedback(feedbackType: 'liked', reset: true);
expect(viewModel.isLoading, false);
expect(viewModel.likedTendersData, null);
expect(viewModel.errorMessage, errorMessage);
});
test('should set loading state during get feedback', () async {
mockRepository.setGetTendersFeedbackResult(
const Result.ok(TestFixtures.likedTendersResponse),
);
final getFeedbackFuture = viewModel.getTendersFeedback(
feedbackType: 'liked',
reset: true,
);
expect(viewModel.isLoading, true);
expect(viewModel.errorMessage, null);
await getFeedbackFuture;
expect(viewModel.isLoading, false);
});
test('should reset data when reset is true', () async {
// First load
mockRepository.setGetTendersFeedbackResult(
const Result.ok(TestFixtures.likedTendersResponse),
);
await viewModel.getTendersFeedback(feedbackType: 'liked', reset: true);
expect(viewModel.likedTendersData, TestFixtures.likedTendersResponse);
// Reset and load again
await viewModel.getTendersFeedback(feedbackType: 'liked', reset: true);
expect(viewModel.likedTendersData, TestFixtures.likedTendersResponse);
});
test('should not load more when already loading', () async {
mockRepository.setGetTendersFeedbackResult(
const Result.ok(TestFixtures.likedTendersResponse),
);
viewModel.isLoadingMore = true;
await viewModel.getTendersFeedback(feedbackType: 'liked', reset: false);
expect(viewModel.likedTendersData, null);
});
test('should not load more when no more data', () async {
mockRepository.setGetTendersFeedbackResult(
const Result.ok(TestFixtures.likedTendersResponse),
);
viewModel.hasMore = false;
await viewModel.getTendersFeedback(feedbackType: 'liked', reset: false);
expect(viewModel.likedTendersData, null);
});
test('should load more feedback successfully', () async {
mockRepository.setGetTendersFeedbackResult(
const Result.ok(TestFixtures.likedTendersResponse),
);
// Set current feedback type
viewModel.getTendersFeedback(feedbackType: 'liked', reset: true);
await viewModel.loadMoreFeedback();
expect(viewModel.likedTendersData, TestFixtures.likedTendersResponse);
});
test('should not load more feedback when no current type', () async {
await viewModel.loadMoreFeedback();
expect(viewModel.likedTendersData, null);
});
});
}
@@ -1,137 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:tm_app/core/utils/result.dart';
import 'package:tm_app/view_models/your_tenders_view_model.dart';
import '../fixtures/test_fixtures.dart';
import '../mocks/mock_your_tenders_repository.dart';
void main() {
group('YourTendersViewModel Filter', () {
late MockYourTendersRepository mockRepository;
late YourTendersViewModel viewModel;
setUp(() {
mockRepository = MockYourTendersRepository();
viewModel = YourTendersViewModel(yourTendersRepository: mockRepository);
});
tearDown(() {
viewModel.dispose();
});
test('should set date range correctly', () {
final dateRange = DateTimeRange(
start: DateTime(2024, 1, 1),
end: DateTime(2024, 1, 31),
);
viewModel.setDateRange(dateRange);
expect(viewModel.selectedDateRange, dateRange);
expect(viewModel.startDateFormatted, '2024/01/01');
expect(viewModel.endDateFormatted, '2024/01/31');
expect(viewModel.startDateUnix, '1704067200');
expect(viewModel.endDateUnix, '1706745600');
});
test('should set status and clear data', () {
// Set some initial data
viewModel.data = TestFixtures.tenderApprovalsResponse;
viewModel.likedTendersData = TestFixtures.likedTendersResponse;
viewModel.isLoading = true;
viewModel.errorMessage = 'Error';
viewModel.setStatus('submitted');
expect(viewModel.selectedStatus, 'submitted');
expect(viewModel.data, null);
expect(viewModel.likedTendersData, null);
expect(viewModel.isLoading, false);
expect(viewModel.errorMessage, null);
expect(viewModel.currentPage, 1);
});
test('should clear filter and reset state', () {
// Set some initial data
final dateRange = DateTimeRange(
start: DateTime(2024, 1, 1),
end: DateTime(2024, 1, 31),
);
viewModel.setDateRange(dateRange);
viewModel.setStatus('submitted');
viewModel.data = TestFixtures.tenderApprovalsResponse;
viewModel.clearFilter();
expect(viewModel.selectedDateRange, null);
expect(viewModel.selectedStatus, 'All');
expect(viewModel.data, null);
expect(viewModel.likedTendersData, null);
expect(viewModel.isLoading, false);
expect(viewModel.errorMessage, null);
});
test('should call with filter for all status', () async {
mockRepository.setGetTendersResult(
const Result.ok(TestFixtures.tenderApprovalsResponse),
);
viewModel.selectedStatus = 'All';
await viewModel.callWithFilter();
expect(viewModel.data, TestFixtures.tenderApprovalsResponse);
expect(viewModel.isLoading, false);
});
test('should call with filter for submitted status', () async {
mockRepository.setGetTendersResult(
const Result.ok(TestFixtures.tenderApprovalsResponse),
);
viewModel.selectedStatus = 'Tenders Submitted';
await viewModel.callWithFilter();
expect(viewModel.data, TestFixtures.tenderApprovalsResponse);
expect(viewModel.isLoading, false);
});
test('should call with filter for liked tenders', () async {
mockRepository.setGetTendersFeedbackResult(
const Result.ok(TestFixtures.likedTendersResponse),
);
viewModel.selectedStatus = 'Like Tenders';
await viewModel.callWithFilter();
expect(viewModel.likedTendersData, TestFixtures.likedTendersResponse);
expect(viewModel.isLoading, false);
});
test('should call with filter for disliked tenders', () async {
mockRepository.setGetTendersFeedbackResult(
const Result.ok(TestFixtures.likedTendersResponse),
);
viewModel.selectedStatus = 'Dislike Tenders';
await viewModel.callWithFilter();
expect(viewModel.likedTendersData, TestFixtures.likedTendersResponse);
expect(viewModel.isLoading, false);
});
test('should format dates correctly with date range', () {
final dateRange = DateTimeRange(
start: DateTime(2024, 12, 25),
end: DateTime(2024, 12, 31),
);
viewModel.setDateRange(dateRange);
expect(viewModel.startDateFormatted, '2024/12/25');
expect(viewModel.endDateFormatted, '2024/12/31');
expect(viewModel.startDateUnix, '1735084800');
expect(viewModel.endDateUnix, '1735689600');
});
});
}
@@ -1,146 +0,0 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:tm_app/core/utils/result.dart';
import 'package:tm_app/data/services/model/request_models/get_tenders_request_model/get_tenders_request_model.dart';
import 'package:tm_app/view_models/your_tenders_view_model.dart';
import '../fixtures/test_fixtures.dart';
import '../mocks/mock_your_tenders_repository.dart';
void main() {
group('YourTendersViewModel GetTenders', () {
late MockYourTendersRepository mockRepository;
late YourTendersViewModel viewModel;
setUp(() {
mockRepository = MockYourTendersRepository();
viewModel = YourTendersViewModel(yourTendersRepository: mockRepository);
});
tearDown(() {
viewModel.dispose();
});
test('should get tenders successfully', () async {
mockRepository.setGetTendersResult(
const Result.ok(TestFixtures.tenderApprovalsResponse),
);
final requestModel = GetTendersRequestModel(
status: 'submitted',
createdFrom: '1640995200',
createdTo: '1640995200',
limit: 10,
offset: 0,
);
await viewModel.getTenders(requestModel: requestModel);
expect(viewModel.isLoading, false);
expect(viewModel.data, TestFixtures.tenderApprovalsResponse);
expect(viewModel.errorMessage, null);
expect(viewModel.hasMoreTenders, true);
});
test('should handle get tenders error', () async {
const errorMessage = 'Network error';
mockRepository.setGetTendersResult(Result.error(Exception(errorMessage)));
final requestModel = GetTendersRequestModel(
status: 'submitted',
createdFrom: '1640995200',
createdTo: '1640995200',
limit: 10,
offset: 0,
);
await viewModel.getTenders(requestModel: requestModel);
expect(viewModel.isLoading, false);
expect(viewModel.data, null);
expect(viewModel.errorMessage, errorMessage);
});
test('should set loading state during get tenders', () async {
mockRepository.setGetTendersResult(
const Result.ok(TestFixtures.tenderApprovalsResponse),
);
final requestModel = GetTendersRequestModel(
status: 'submitted',
createdFrom: '1640995200',
createdTo: '1640995200',
limit: 10,
offset: 0,
);
final getTendersFuture = viewModel.getTenders(requestModel: requestModel);
expect(viewModel.isLoading, true);
expect(viewModel.errorMessage, null);
await getTendersFuture;
expect(viewModel.isLoading, false);
});
test('should reset data when reset is true', () async {
// First load
mockRepository.setGetTendersResult(
const Result.ok(TestFixtures.tenderApprovalsResponse),
);
final requestModel = GetTendersRequestModel(
status: 'submitted',
createdFrom: '1640995200',
createdTo: '1640995200',
limit: 10,
offset: 0,
);
await viewModel.getTenders(requestModel: requestModel);
expect(viewModel.data, TestFixtures.tenderApprovalsResponse);
// Reset and load again
await viewModel.getTenders(requestModel: requestModel, reset: true);
expect(viewModel.data, TestFixtures.tenderApprovalsResponse);
});
test('should not load more when already loading', () async {
mockRepository.setGetTendersResult(
const Result.ok(TestFixtures.tenderApprovalsResponse),
);
final requestModel = GetTendersRequestModel(
status: 'submitted',
createdFrom: '1640995200',
createdTo: '1640995200',
limit: 10,
offset: 0,
);
viewModel.isLoadingMoreTenders = true;
await viewModel.getTenders(requestModel: requestModel);
expect(viewModel.data, null);
});
test('should not load more when no more data', () async {
mockRepository.setGetTendersResult(
const Result.ok(TestFixtures.tenderApprovalsResponse),
);
final requestModel = GetTendersRequestModel(
status: 'submitted',
createdFrom: '1640995200',
createdTo: '1640995200',
limit: 10,
offset: 0,
);
viewModel.hasMoreTenders = false;
await viewModel.getTenders(requestModel: requestModel);
expect(viewModel.data, null);
});
});
}
@@ -1,45 +0,0 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:tm_app/view_models/your_tenders_view_model.dart';
import '../mocks/mock_your_tenders_repository.dart';
void main() {
group('YourTendersViewModel Initialization', () {
late MockYourTendersRepository mockRepository;
late YourTendersViewModel viewModel;
setUp(() {
mockRepository = MockYourTendersRepository();
viewModel = YourTendersViewModel(yourTendersRepository: mockRepository);
});
tearDown(() {
viewModel.dispose();
});
test('should initialize with default values', () {
expect(viewModel.isLoading, false);
expect(viewModel.errorMessage, null);
expect(viewModel.data, null);
expect(viewModel.likedTendersData, null);
expect(viewModel.isLoadingMore, false);
expect(viewModel.hasMore, true);
expect(viewModel.isLoadingMoreTenders, false);
expect(viewModel.hasMoreTenders, true);
expect(viewModel.selectedDateRange, null);
expect(viewModel.selectedStatus, 'All');
expect(viewModel.currentPage, 1);
});
test('should format dates correctly when no date range is selected', () {
expect(viewModel.startDateFormatted, 'YYYY/MM/DD');
expect(viewModel.endDateFormatted, 'YYYY/MM/DD');
expect(viewModel.startDateUnix, '');
expect(viewModel.endDateUnix, '');
});
test('should calculate total pages correctly when no data', () {
expect(viewModel.totalPages, 1);
});
});
}
@@ -1,116 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:tm_app/core/utils/result.dart';
import 'package:tm_app/view_models/your_tenders_view_model.dart';
import '../fixtures/test_fixtures.dart';
import '../mocks/mock_your_tenders_repository.dart';
void main() {
group('YourTendersViewModel Pagination', () {
late MockYourTendersRepository mockRepository;
late YourTendersViewModel viewModel;
setUp(() {
mockRepository = MockYourTendersRepository();
viewModel = YourTendersViewModel(yourTendersRepository: mockRepository);
});
tearDown(() {
viewModel.dispose();
});
test('should calculate total pages from tender data', () {
viewModel.data = TestFixtures.tenderApprovalsResponse;
expect(viewModel.totalPages, 1);
});
test('should calculate total pages from feedback data', () {
viewModel.likedTendersData = TestFixtures.likedTendersResponse;
expect(viewModel.totalPages, 1);
});
test('should return 1 when no data available', () {
expect(viewModel.totalPages, 1);
});
test('should jump to page successfully', () async {
mockRepository.setGetTendersResult(
const Result.ok(TestFixtures.tenderApprovalsResponse),
);
viewModel.selectedStatus = 'All';
await viewModel.jumpToPage(2);
expect(viewModel.currentPage, 2);
expect(viewModel.data, TestFixtures.tenderApprovalsResponse);
expect(viewModel.likedTendersData, null);
expect(viewModel.hasMore, true);
expect(viewModel.hasMoreTenders, true);
});
test('should load more tenders for all status', () async {
mockRepository.setGetTendersResult(
const Result.ok(TestFixtures.tenderApprovalsResponse),
);
viewModel.selectedStatus = 'All';
await viewModel.loadMoreTenders();
expect(viewModel.data, TestFixtures.tenderApprovalsResponse);
});
test('should load more tenders for submitted status', () async {
mockRepository.setGetTendersResult(
const Result.ok(TestFixtures.tenderApprovalsResponse),
);
viewModel.selectedStatus = 'Tenders Submitted';
await viewModel.loadMoreTenders();
expect(viewModel.data, TestFixtures.tenderApprovalsResponse);
});
test('should not load more tenders when already loading', () async {
viewModel.isLoadingMoreTenders = true;
await viewModel.loadMoreTenders();
expect(viewModel.data, null);
});
test('should not load more tenders when no more data', () async {
viewModel.hasMoreTenders = false;
await viewModel.loadMoreTenders();
expect(viewModel.data, null);
});
test('should handle pagination with date range', () async {
mockRepository.setGetTendersResult(
const Result.ok(TestFixtures.tenderApprovalsResponse),
);
viewModel.selectedStatus = 'All';
viewModel.setDateRange(
DateTimeRange(start: DateTime(2024, 1, 1), end: DateTime(2024, 1, 31)),
);
await viewModel.loadMoreTenders();
expect(viewModel.data, TestFixtures.tenderApprovalsResponse);
});
test('should reset pagination state when clearing data', () {
// Set some pagination state
viewModel.currentPage = 3;
viewModel.data = TestFixtures.tenderApprovalsResponse;
viewModel.likedTendersData = TestFixtures.likedTendersResponse;
viewModel.clearFilter();
expect(viewModel.currentPage, 1);
expect(viewModel.data, null);
expect(viewModel.likedTendersData, null);
});
});
}
@@ -1,199 +0,0 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:tm_app/core/constants/assets.dart';
import 'package:tm_app/core/constants/tender_approval_status.dart';
import 'package:tm_app/core/constants/tender_feedback.dart';
import 'package:tm_app/core/theme/colors.dart';
import 'package:tm_app/view_models/your_tenders_view_model.dart';
import '../mocks/mock_your_tenders_repository.dart';
void main() {
group('YourTendersViewModel Utility Methods', () {
late MockYourTendersRepository mockRepository;
late YourTendersViewModel viewModel;
setUp(() {
mockRepository = MockYourTendersRepository();
viewModel = YourTendersViewModel(yourTendersRepository: mockRepository);
});
tearDown(() {
viewModel.dispose();
});
group('getBackGroundColorByStatus', () {
test('should return green for liked status', () {
final color = viewModel.getBackGroundColorByStatus(
TenderFeedback.liked.value,
);
expect(color, AppColors.green0);
});
test('should return green for submitted status', () {
final color = viewModel.getBackGroundColorByStatus(
TenderApprovalStatus.submitted.value,
);
expect(color, AppColors.green0);
});
test('should return red for disliked status', () {
final color = viewModel.getBackGroundColorByStatus(
TenderFeedback.disliked.value,
);
expect(color, AppColors.red0);
});
test('should return red for rejected status', () {
final color = viewModel.getBackGroundColorByStatus(
TenderApprovalStatus.rejected.value,
);
expect(color, AppColors.red0);
});
test('should return grey for unknown status', () {
final color = viewModel.getBackGroundColorByStatus('unknown');
expect(color, AppColors.grey0);
});
});
group('getBorderColorByStatus', () {
test('should return green for liked status', () {
final color = viewModel.getBorderColorByStatus(
TenderFeedback.liked.value,
);
expect(color, AppColors.green10);
});
test('should return green for submitted status', () {
final color = viewModel.getBorderColorByStatus(
TenderApprovalStatus.submitted.value,
);
expect(color, AppColors.green10);
});
test('should return red for disliked status', () {
final color = viewModel.getBorderColorByStatus(
TenderFeedback.disliked.value,
);
expect(color, AppColors.red10);
});
test('should return red for rejected status', () {
final color = viewModel.getBorderColorByStatus(
TenderApprovalStatus.rejected.value,
);
expect(color, AppColors.red10);
});
test('should return grey for unknown status', () {
final color = viewModel.getBorderColorByStatus('unknown');
expect(color, AppColors.grey30);
});
});
group('getStatusCardColorByStatus', () {
test('should return green for liked status', () {
final color = viewModel.getStatusCardColorByStatus(
TenderFeedback.liked.value,
);
expect(color, AppColors.green20);
});
test('should return green for submitted status', () {
final color = viewModel.getStatusCardColorByStatus(
TenderApprovalStatus.submitted.value,
);
expect(color, AppColors.green20);
});
test('should return red for disliked status', () {
final color = viewModel.getStatusCardColorByStatus(
TenderFeedback.disliked.value,
);
expect(color, AppColors.red10);
});
test('should return red for rejected status', () {
final color = viewModel.getStatusCardColorByStatus(
TenderApprovalStatus.rejected.value,
);
expect(color, AppColors.red10);
});
test('should return grey for unknown status', () {
final color = viewModel.getStatusCardColorByStatus('unknown');
expect(color, AppColors.grey0);
});
});
group('getStatusTextColorByStatus', () {
test('should return green for liked status', () {
final color = viewModel.getStatusTextColorByStatus(
TenderFeedback.liked.value,
);
expect(color, AppColors.green30);
});
test('should return green for submitted status', () {
final color = viewModel.getStatusTextColorByStatus(
TenderApprovalStatus.submitted.value,
);
expect(color, AppColors.green30);
});
test('should return red for disliked status', () {
final color = viewModel.getStatusTextColorByStatus(
TenderFeedback.disliked.value,
);
expect(color, AppColors.red20);
});
test('should return red for rejected status', () {
final color = viewModel.getStatusTextColorByStatus(
TenderApprovalStatus.rejected.value,
);
expect(color, AppColors.red20);
});
test('should return grey for unknown status', () {
final color = viewModel.getStatusTextColorByStatus('unknown');
expect(color, AppColors.grey60);
});
});
group('getStatusIconByStatus', () {
test('should return tick circle for liked status', () {
final icon = viewModel.getStatusIconByStatus(
TenderFeedback.liked.value,
);
expect(icon, AssetsManager.tickCircle);
});
test('should return tick circle for submitted status', () {
final icon = viewModel.getStatusIconByStatus(
TenderApprovalStatus.submitted.value,
);
expect(icon, AssetsManager.tickCircle);
});
test('should return close circle for disliked status', () {
final icon = viewModel.getStatusIconByStatus(
TenderFeedback.disliked.value,
);
expect(icon, AssetsManager.closeCircle);
});
test('should return close circle for rejected status', () {
final icon = viewModel.getStatusIconByStatus(
TenderApprovalStatus.rejected.value,
);
expect(icon, AssetsManager.closeCircle);
});
test('should return null for unknown status', () {
final icon = viewModel.getStatusIconByStatus('unknown');
expect(icon, null);
});
});
});
}