fix: adjustment on the notification and loading
This commit is contained in:
parent
048410786b
commit
e3d2cbb7a6
|
@ -38,6 +38,15 @@ android {
|
||||||
storePassword = "uppercase12"
|
storePassword = "uppercase12"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
release {
|
||||||
|
keyAlias = "genso-prod"
|
||||||
|
keyPassword = "oukenzeumasio"
|
||||||
|
storeFile = file("my-release-key.jks")
|
||||||
|
storePassword = "oukenzeumasio"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
|
@ -46,6 +55,10 @@ android {
|
||||||
// Signing with the debug keys for now, so `flutter run --release` works.
|
// Signing with the debug keys for now, so `flutter run --release` works.
|
||||||
signingConfig = signingConfigs.debug
|
signingConfig = signingConfigs.debug
|
||||||
}
|
}
|
||||||
|
|
||||||
|
release {
|
||||||
|
signingConfig = signingConfigs.release
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
class APIEndpoint {
|
class APIEndpoint {
|
||||||
static const String baseUrl = "http://192.168.1.9:5000";
|
// static const String baseUrl = "http://192.168.1.9:5000";
|
||||||
// static const String baseUrl = "http://172.16.106.133:5000";
|
static const String baseUrl = "http://103.193.178.121:5000";
|
||||||
static const String api = "$baseUrl/api";
|
static const String api = "$baseUrl/api";
|
||||||
|
|
||||||
static const String login = "/login";
|
static const String login = "/login";
|
||||||
|
|
|
@ -7,17 +7,17 @@ import 'package:quiz_app/data/models/history/participant_history_result.dart';
|
||||||
import 'package:quiz_app/data/providers/dio_client.dart';
|
import 'package:quiz_app/data/providers/dio_client.dart';
|
||||||
|
|
||||||
class AnswerService extends GetxService {
|
class AnswerService extends GetxService {
|
||||||
late final Dio _dio;
|
late final Dio dio;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
_dio = Get.find<ApiClient>().dio;
|
dio = Get.find<ApiClient>().dio;
|
||||||
super.onInit();
|
super.onInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<BaseResponseModel?> submitQuizAnswers(Map<String, dynamic> payload) async {
|
Future<BaseResponseModel?> submitQuizAnswers(Map<String, dynamic> payload) async {
|
||||||
try {
|
try {
|
||||||
await _dio.post(
|
await dio.post(
|
||||||
APIEndpoint.quizAnswer,
|
APIEndpoint.quizAnswer,
|
||||||
data: payload,
|
data: payload,
|
||||||
);
|
);
|
||||||
|
@ -30,7 +30,7 @@ class AnswerService extends GetxService {
|
||||||
|
|
||||||
Future<BaseResponseModel<ParticipantResult>?> getAnswerSession(String sessionId, String userId) async {
|
Future<BaseResponseModel<ParticipantResult>?> getAnswerSession(String sessionId, String userId) async {
|
||||||
try {
|
try {
|
||||||
final response = await _dio.post(APIEndpoint.quizAnswerSession, data: {
|
final response = await dio.post(APIEndpoint.quizAnswerSession, data: {
|
||||||
"session_id": sessionId,
|
"session_id": sessionId,
|
||||||
"user_id": userId,
|
"user_id": userId,
|
||||||
});
|
});
|
||||||
|
|
|
@ -9,17 +9,17 @@ import 'package:quiz_app/data/models/quiz/quiz_listing_model.dart';
|
||||||
import 'package:quiz_app/data/providers/dio_client.dart';
|
import 'package:quiz_app/data/providers/dio_client.dart';
|
||||||
|
|
||||||
class QuizService extends GetxService {
|
class QuizService extends GetxService {
|
||||||
late final Dio _dio;
|
late final Dio dio;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
_dio = Get.find<ApiClient>().dio;
|
dio = Get.find<ApiClient>().dio;
|
||||||
super.onInit();
|
super.onInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> createQuiz(QuizCreateRequestModel request) async {
|
Future<bool> createQuiz(QuizCreateRequestModel request) async {
|
||||||
try {
|
try {
|
||||||
final response = await _dio.post(
|
final response = await dio.post(
|
||||||
APIEndpoint.quiz,
|
APIEndpoint.quiz,
|
||||||
data: request.toJson(),
|
data: request.toJson(),
|
||||||
);
|
);
|
||||||
|
@ -37,7 +37,7 @@ class QuizService extends GetxService {
|
||||||
|
|
||||||
Future<BaseResponseModel<List<RawQuizModel>>> createQuizAuto(String sentence) async {
|
Future<BaseResponseModel<List<RawQuizModel>>> createQuizAuto(String sentence) async {
|
||||||
try {
|
try {
|
||||||
final response = await _dio.post(
|
final response = await dio.post(
|
||||||
APIEndpoint.quizGenerate,
|
APIEndpoint.quizGenerate,
|
||||||
data: {"sentence": sentence},
|
data: {"sentence": sentence},
|
||||||
);
|
);
|
||||||
|
@ -63,7 +63,7 @@ class QuizService extends GetxService {
|
||||||
|
|
||||||
Future<BaseResponseModel<List<QuizListingModel>>?> userQuiz(String userId, int page) async {
|
Future<BaseResponseModel<List<QuizListingModel>>?> userQuiz(String userId, int page) async {
|
||||||
try {
|
try {
|
||||||
final response = await _dio.get("${APIEndpoint.userQuiz}/$userId?page=$page");
|
final response = await dio.get("${APIEndpoint.userQuiz}/$userId?page=$page");
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
final parsedResponse = BaseResponseModel<List<QuizListingModel>>.fromJson(
|
final parsedResponse = BaseResponseModel<List<QuizListingModel>>.fromJson(
|
||||||
response.data,
|
response.data,
|
||||||
|
@ -82,7 +82,7 @@ class QuizService extends GetxService {
|
||||||
|
|
||||||
Future<BaseResponseModel<List<QuizListingModel>>?> recomendationQuiz({int page = 1, int amount = 3}) async {
|
Future<BaseResponseModel<List<QuizListingModel>>?> recomendationQuiz({int page = 1, int amount = 3}) async {
|
||||||
try {
|
try {
|
||||||
final response = await _dio.get("${APIEndpoint.quizRecomendation}?page=$page&limit=$amount");
|
final response = await dio.get("${APIEndpoint.quizRecomendation}?page=$page&limit=$amount");
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
final parsedResponse = BaseResponseModel<List<QuizListingModel>>.fromJson(
|
final parsedResponse = BaseResponseModel<List<QuizListingModel>>.fromJson(
|
||||||
|
@ -110,7 +110,7 @@ class QuizService extends GetxService {
|
||||||
};
|
};
|
||||||
|
|
||||||
final uri = Uri.parse(APIEndpoint.quizSearch).replace(queryParameters: queryParams);
|
final uri = Uri.parse(APIEndpoint.quizSearch).replace(queryParameters: queryParams);
|
||||||
final response = await _dio.getUri(uri);
|
final response = await dio.getUri(uri);
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
final parsedResponse = BaseResponseModel<List<QuizListingModel>>.fromJson(
|
final parsedResponse = BaseResponseModel<List<QuizListingModel>>.fromJson(
|
||||||
|
@ -130,7 +130,7 @@ class QuizService extends GetxService {
|
||||||
|
|
||||||
Future<BaseResponseModel<QuizData>?> getQuizById(String quizId) async {
|
Future<BaseResponseModel<QuizData>?> getQuizById(String quizId) async {
|
||||||
try {
|
try {
|
||||||
final response = await _dio.get("${APIEndpoint.quiz}/$quizId");
|
final response = await dio.get("${APIEndpoint.quiz}/$quizId");
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
final parsedResponse = BaseResponseModel<QuizData>.fromJson(
|
final parsedResponse = BaseResponseModel<QuizData>.fromJson(
|
||||||
|
|
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:quiz_app/app/routes/app_pages.dart';
|
import 'package:quiz_app/app/routes/app_pages.dart';
|
||||||
import 'package:quiz_app/component/global_button.dart';
|
import 'package:quiz_app/component/global_button.dart';
|
||||||
|
import 'package:quiz_app/core/utils/custom_notification.dart';
|
||||||
import 'package:quiz_app/core/utils/logger.dart';
|
import 'package:quiz_app/core/utils/logger.dart';
|
||||||
import 'package:quiz_app/data/controllers/user_controller.dart';
|
import 'package:quiz_app/data/controllers/user_controller.dart';
|
||||||
import 'package:quiz_app/data/entity/user/user_entity.dart';
|
import 'package:quiz_app/data/entity/user/user_entity.dart';
|
||||||
|
@ -74,7 +75,7 @@ class LoginController extends GetxController {
|
||||||
Get.offAllNamed(AppRoutes.mainPage);
|
Get.offAllNamed(AppRoutes.mainPage);
|
||||||
} catch (e, stackTrace) {
|
} catch (e, stackTrace) {
|
||||||
logC.e(e, stackTrace: stackTrace);
|
logC.e(e, stackTrace: stackTrace);
|
||||||
Get.snackbar("Error", "Failed to connect to server");
|
CustomNotification.error(title: "failed", message: "Check username and password");
|
||||||
} finally {
|
} finally {
|
||||||
isLoading.value = false;
|
isLoading.value = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:quiz_app/core/utils/custom_floating_loading.dart';
|
||||||
|
import 'package:quiz_app/core/utils/custom_notification.dart';
|
||||||
import 'package:quiz_app/data/models/register/register_request.dart';
|
import 'package:quiz_app/data/models/register/register_request.dart';
|
||||||
import 'package:quiz_app/data/services/auth_service.dart';
|
import 'package:quiz_app/data/services/auth_service.dart';
|
||||||
|
|
||||||
|
@ -59,6 +61,7 @@ class RegisterController extends GetxController {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
CustomFloatingLoading.showLoadingDialog(Get.context!);
|
||||||
await _authService.register(
|
await _authService.register(
|
||||||
RegisterRequestModel(
|
RegisterRequestModel(
|
||||||
email: email,
|
email: email,
|
||||||
|
@ -68,7 +71,10 @@ class RegisterController extends GetxController {
|
||||||
phone: phone,
|
phone: phone,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
Get.back();
|
Get.back();
|
||||||
|
CustomFloatingLoading.hideLoadingDialog(Get.context!);
|
||||||
|
CustomNotification.success(title: "register success", message: "created account successfuly");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Get.snackbar("Error", "Failed to register: ${e.toString()}");
|
Get.snackbar("Error", "Failed to register: ${e.toString()}");
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,128 @@
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:mocktail/mocktail.dart';
|
||||||
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:quiz_app/core/endpoint/api_endpoint.dart';
|
||||||
|
import 'package:quiz_app/data/models/base/base_model.dart';
|
||||||
|
import 'package:quiz_app/data/services/answer_service.dart';
|
||||||
|
|
||||||
|
class MockDio extends Mock implements Dio {}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
late MockDio mockDio;
|
||||||
|
late AnswerService answerService;
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
mockDio = MockDio();
|
||||||
|
answerService = AnswerService();
|
||||||
|
answerService.dio = mockDio;
|
||||||
|
});
|
||||||
|
|
||||||
|
group('AnswerService Tests', () {
|
||||||
|
test('submitQuizAnswers - Success', () async {
|
||||||
|
final payload = {'question_id': 'q1', 'answer': 'A'};
|
||||||
|
|
||||||
|
when(() => mockDio.post(APIEndpoint.quizAnswer, data: payload)).thenAnswer((_) async => Response(
|
||||||
|
statusCode: 200,
|
||||||
|
data: {},
|
||||||
|
requestOptions: RequestOptions(path: APIEndpoint.quizAnswer),
|
||||||
|
));
|
||||||
|
|
||||||
|
final result = await answerService.submitQuizAnswers(payload);
|
||||||
|
|
||||||
|
expect(result, isA<BaseResponseModel>());
|
||||||
|
expect(result?.message, 'success');
|
||||||
|
|
||||||
|
verify(() => mockDio.post(APIEndpoint.quizAnswer, data: payload)).called(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('submitQuizAnswers - Failure', () async {
|
||||||
|
final payload = {'question_id': 'q1', 'answer': 'A'};
|
||||||
|
|
||||||
|
when(() => mockDio.post(APIEndpoint.quizAnswer, data: payload)).thenThrow(DioException(
|
||||||
|
requestOptions: RequestOptions(path: APIEndpoint.quizAnswer),
|
||||||
|
error: 'Network Error',
|
||||||
|
type: DioExceptionType.connectionError,
|
||||||
|
));
|
||||||
|
|
||||||
|
final result = await answerService.submitQuizAnswers(payload);
|
||||||
|
|
||||||
|
expect(result, isNull);
|
||||||
|
verify(() => mockDio.post(APIEndpoint.quizAnswer, data: payload)).called(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getAnswerSession - Success', () async {
|
||||||
|
final sessionId = '682a26b3bedac6c20a215452';
|
||||||
|
final userId = '680f0e63180b5c19b3751d42';
|
||||||
|
final responseData = {
|
||||||
|
"message": "Successfully retrieved the answer",
|
||||||
|
"data": {
|
||||||
|
"id": "682a26e6bedac6c20a215453",
|
||||||
|
"session_id": "682a26b3bedac6c20a215452",
|
||||||
|
"quiz_id": "682a120f18339f4cc31318e4",
|
||||||
|
"user_id": "680f0e63180b5c19b3751d42",
|
||||||
|
"answered_at": "2025-05-19 01:28:22",
|
||||||
|
"answers": [
|
||||||
|
{
|
||||||
|
"index": 1,
|
||||||
|
"question": "Siapakah ketua Wali Songo yang juga dikenal sebagai Sunan Gresik?",
|
||||||
|
"target_answer": "Maulana Malik Ibrahim",
|
||||||
|
"duration": 30,
|
||||||
|
"type": "fill_the_blank",
|
||||||
|
"options": null,
|
||||||
|
"answer": "maulana Malik ibrahim",
|
||||||
|
"is_correct": true,
|
||||||
|
"time_spent": 8.0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"total_score": 100,
|
||||||
|
"total_correct": 1
|
||||||
|
},
|
||||||
|
"meta": null
|
||||||
|
};
|
||||||
|
|
||||||
|
when(() => mockDio.post(APIEndpoint.quizAnswerSession, data: {
|
||||||
|
"session_id": sessionId,
|
||||||
|
"user_id": userId,
|
||||||
|
})).thenAnswer((_) async => Response(
|
||||||
|
statusCode: 200,
|
||||||
|
data: responseData,
|
||||||
|
requestOptions: RequestOptions(path: APIEndpoint.quizAnswerSession),
|
||||||
|
));
|
||||||
|
|
||||||
|
final result = await answerService.getAnswerSession(sessionId, userId);
|
||||||
|
|
||||||
|
expect(result, isNotNull);
|
||||||
|
expect(result?.data?.sessionId, sessionId);
|
||||||
|
expect(result?.data?.userId, userId);
|
||||||
|
expect(result?.data?.totalScore, 100);
|
||||||
|
|
||||||
|
verify(() => mockDio.post(APIEndpoint.quizAnswerSession, data: {
|
||||||
|
"session_id": sessionId,
|
||||||
|
"user_id": userId,
|
||||||
|
})).called(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getAnswerSession - Failure', () async {
|
||||||
|
final sessionId = '';
|
||||||
|
final userId = '';
|
||||||
|
|
||||||
|
when(() => mockDio.post(APIEndpoint.quizAnswerSession, data: {
|
||||||
|
"session_id": sessionId,
|
||||||
|
"user_id": userId,
|
||||||
|
})).thenThrow(DioException(
|
||||||
|
requestOptions: RequestOptions(path: APIEndpoint.quizAnswerSession),
|
||||||
|
error: 'Network Error',
|
||||||
|
type: DioExceptionType.connectionError,
|
||||||
|
));
|
||||||
|
|
||||||
|
final result = await answerService.getAnswerSession(sessionId, userId);
|
||||||
|
|
||||||
|
expect(result, isNull);
|
||||||
|
|
||||||
|
verify(() => mockDio.post(APIEndpoint.quizAnswerSession, data: {
|
||||||
|
"session_id": sessionId,
|
||||||
|
"user_id": userId,
|
||||||
|
})).called(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,114 @@
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:mocktail/mocktail.dart';
|
||||||
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:quiz_app/data/models/quiz/question_listings_model.dart';
|
||||||
|
import 'package:quiz_app/data/providers/dio_client.dart';
|
||||||
|
import 'package:quiz_app/data/services/quiz_service.dart';
|
||||||
|
import 'package:quiz_app/data/models/quiz/question_create_request.dart';
|
||||||
|
|
||||||
|
class MockDio extends Mock implements Dio {}
|
||||||
|
|
||||||
|
class MockApiClient extends Mock implements ApiClient {}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
late MockDio mockDio;
|
||||||
|
late QuizService quizService;
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
mockDio = MockDio();
|
||||||
|
quizService = QuizService();
|
||||||
|
quizService.dio = mockDio;
|
||||||
|
});
|
||||||
|
|
||||||
|
group('createQuiz', () {
|
||||||
|
final request = QuizCreateRequestModel(
|
||||||
|
title: 'Test Quiz',
|
||||||
|
description: 'A sample quiz description',
|
||||||
|
isPublic: true,
|
||||||
|
date: '2025-05-19',
|
||||||
|
totalQuiz: 1,
|
||||||
|
limitDuration: 60,
|
||||||
|
authorId: 'author_123',
|
||||||
|
subjectId: 'subject_456',
|
||||||
|
questionListings: [
|
||||||
|
QuestionListing(
|
||||||
|
index: 1,
|
||||||
|
question: 'Sample question?',
|
||||||
|
targetAnswer: 'Sample Answer',
|
||||||
|
duration: 30,
|
||||||
|
type: 'multiple_choice',
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
test('returns true when status code is 201', () async {
|
||||||
|
when(() => mockDio.post(any(), data: any(named: 'data'))).thenAnswer(
|
||||||
|
(_) async => Response(
|
||||||
|
requestOptions: RequestOptions(path: ''),
|
||||||
|
statusCode: 201,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final result = await quizService.createQuiz(request);
|
||||||
|
expect(result, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('throws Exception on non-201 response', () async {
|
||||||
|
when(() => mockDio.post(any(), data: any(named: 'data'))).thenAnswer(
|
||||||
|
(_) async => Response(
|
||||||
|
requestOptions: RequestOptions(path: ''),
|
||||||
|
statusCode: 400,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(() => quizService.createQuiz(request), throwsException);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('throws Exception on Dio error', () async {
|
||||||
|
when(() => mockDio.post(any(), data: any(named: 'data'))).thenThrow(Exception('Network Error'));
|
||||||
|
|
||||||
|
expect(() => quizService.createQuiz(request), throwsException);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group('createQuizAuto', () {
|
||||||
|
const sentence = "This is a test sentence.";
|
||||||
|
final mockResponseData = {
|
||||||
|
'message': "succes create quiz automatic",
|
||||||
|
'data': [
|
||||||
|
{'qustion': 'What is this?', 'answer': 'A test.'},
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
test('returns BaseResponseModel when status code is 200', () async {
|
||||||
|
when(() => mockDio.post(any(), data: any(named: 'data'))).thenAnswer(
|
||||||
|
(_) async => Response(
|
||||||
|
requestOptions: RequestOptions(path: ''),
|
||||||
|
statusCode: 200,
|
||||||
|
data: mockResponseData,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final result = await quizService.createQuizAuto(sentence);
|
||||||
|
expect(result.data, isA<List<RawQuizModel>>());
|
||||||
|
expect(result.data!.first.qustion, 'What is this?');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('throws Exception on non-200 response', () async {
|
||||||
|
when(() => mockDio.post(any(), data: any(named: 'data'))).thenAnswer(
|
||||||
|
(_) async => Response(
|
||||||
|
requestOptions: RequestOptions(path: ''),
|
||||||
|
statusCode: 500,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(() => quizService.createQuizAuto(sentence), throwsException);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('throws Exception on Dio error', () async {
|
||||||
|
when(() => mockDio.post(any(), data: any(named: 'data'))).thenThrow(Exception('Network Error'));
|
||||||
|
|
||||||
|
expect(() => quizService.createQuizAuto(sentence), throwsException);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in New Issue