diff --git a/lib/api_service.dart b/lib/api_service.dart index d69e6a24..176c04ce 100644 --- a/lib/api_service.dart +++ b/lib/api_service.dart @@ -71,18 +71,23 @@ String baseUrl = 'http://YOUR_IP:8000'; String origin = 'http://localhost:8080'; Future> fetchTasks(String uuid, String encryptionSecret) async { - String url = - '$baseUrl/tasks?email=email&origin=$origin&UUID=$uuid&encryptionSecret=$encryptionSecret'; - - var response = await http.get(Uri.parse(url), headers: { - "Content-Type": "application/json", - }).timeout(const Duration(seconds: 10000)); - if (response.statusCode == 200) { - List allTasks = jsonDecode(response.body); - debugPrint(allTasks.toString()); - return allTasks.map((task) => Tasks.fromJson(task)).toList(); - } else { - throw Exception('Failed to load tasks'); + try { + String url = + '$baseUrl/tasks?email=email&origin=$origin&UUID=$uuid&encryptionSecret=$encryptionSecret'; + + var response = await http.get(Uri.parse(url), headers: { + "Content-Type": "application/json", + }).timeout(const Duration(seconds: 10000)); + if (response.statusCode == 200) { + List allTasks = jsonDecode(response.body); + debugPrint(allTasks.toString()); + return allTasks.map((task) => Tasks.fromJson(task)).toList(); + } else { + throw Exception('Failed to load tasks'); + } + } catch (e) { + debugPrint('Error fetching tasks: $e'); + return []; } } diff --git a/pubspec.yaml b/pubspec.yaml index 76c5869d..19db109c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -69,6 +69,8 @@ dev_dependencies: build_runner: null flutter_gen_runner: null flutter_lints: 4.0.0 + http_mock_adapter: ^0.3.0 + sqflite_common_ffi: ^2.0.0 flutter_gen: output: lib/app/utils/gen/ diff --git a/test/api_service_test.dart b/test/api_service_test.dart new file mode 100644 index 00000000..ba252fac --- /dev/null +++ b/test/api_service_test.dart @@ -0,0 +1,164 @@ +import 'dart:convert'; + +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; +import 'package:http/http.dart' as http; +import 'package:sqflite_common_ffi/sqflite_ffi.dart'; +import 'package:taskwarrior/api_service.dart'; +import 'package:taskwarrior/app/utils/taskchampion/credentials_storage.dart'; + +import 'api_service_test.mocks.dart'; + +class MockCredentialsStorage extends Mock implements CredentialsStorage {} + +class MockMethodChannel extends Mock implements MethodChannel {} + +@GenerateMocks([MockMethodChannel, http.Client]) +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + databaseFactory = databaseFactoryFfi; + MockClient mockClient = MockClient(); + + setUpAll(() { + sqfliteFfiInit(); + }); + + group('Tasks model', () { + test('fromJson creates Tasks object', () { + final json = { + 'id': 1, + 'description': 'Task 1', + 'project': 'Project 1', + 'status': 'pending', + 'uuid': '123', + 'urgency': 5.0, + 'priority': 'H', + 'due': '2024-12-31', + 'end': null, + 'entry': '2024-01-01', + 'modified': '2024-11-01', + }; + + final task = Tasks.fromJson(json); + + expect(task.id, 1); + expect(task.description, 'Task 1'); + expect(task.project, 'Project 1'); + expect(task.status, 'pending'); + expect(task.uuid, '123'); + expect(task.urgency, 5.0); + expect(task.priority, 'H'); + expect(task.due, '2024-12-31'); + expect(task.entry, '2024-01-01'); + expect(task.modified, '2024-11-01'); + }); + + test('toJson converts Tasks object to JSON', () { + final task = Tasks( + id: 1, + description: 'Task 1', + project: 'Project 1', + status: 'pending', + uuid: '123', + urgency: 5.0, + priority: 'H', + due: '2024-12-31', + end: null, + entry: '2024-01-01', + modified: '2024-11-01', + ); + + final json = task.toJson(); + + expect(json['id'], 1); + expect(json['description'], 'Task 1'); + expect(json['project'], 'Project 1'); + expect(json['status'], 'pending'); + expect(json['uuid'], '123'); + expect(json['urgency'], 5.0); + expect(json['priority'], 'H'); + expect(json['due'], '2024-12-31'); + }); + }); + + group('fetchTasks', () { + test('Fetch data successfully', () async { + final responseJson = jsonEncode({'data': 'Mock data'}); + when(mockClient.get( + Uri.parse( + '$baseUrl/tasks?email=email&origin=$origin&UUID=123&encryptionSecret=secret'), + headers: { + "Content-Type": "application/json", + })).thenAnswer((_) async => http.Response(responseJson, 200)); + + final result = await fetchTasks('123', 'secret'); + + expect(result, isA>()); + }); + + test('fetchTasks returns empty array', () async { + const uuid = '123'; + const encryptionSecret = 'secret'; + + expect(await fetchTasks(uuid, encryptionSecret), isEmpty); + }); + }); + + group('TaskDatabase', () { + late TaskDatabase taskDatabase; + + setUp(() async { + taskDatabase = TaskDatabase(); + await taskDatabase.open(); + }); + + test('insertTask adds a task to the database', () async { + final task = Tasks( + id: 1, + description: 'Task 1', + project: 'Project 1', + status: 'pending', + uuid: '123', + urgency: 5.0, + priority: 'H', + due: '2024-12-31', + end: null, + entry: '2024-01-01', + modified: '2024-11-01', + ); + + await taskDatabase.insertTask(task); + + final tasks = await taskDatabase.fetchTasksFromDatabase(); + + expect(tasks.length, 1); + expect(tasks[0].description, 'Task 1'); + }); + + test('deleteAllTasksInDB removes all tasks', () async { + final task = Tasks( + id: 1, + description: 'Task 1', + project: 'Project 1', + status: 'pending', + uuid: '123', + urgency: 5.0, + priority: 'H', + due: '2024-12-31', + end: null, + entry: '2024-01-01', + modified: '2024-11-01', + ); + + await taskDatabase.insertTask(task); + await taskDatabase.deleteAllTasksInDB(); + + final tasks = await taskDatabase.fetchTasksFromDatabase(); + + expect(tasks.length, 0); + }); + }); +} diff --git a/test/api_service_test.mocks.dart b/test/api_service_test.mocks.dart new file mode 100644 index 00000000..c23b5109 --- /dev/null +++ b/test/api_service_test.mocks.dart @@ -0,0 +1,401 @@ +// Mocks generated by Mockito 5.4.4 from annotations +// in taskwarrior/test/api_service_test.dart. +// Do not manually edit this file. + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'dart:async' as _i6; +import 'dart:convert' as _i7; +import 'dart:typed_data' as _i8; + +import 'package:flutter/services.dart' as _i2; +import 'package:http/http.dart' as _i3; +import 'package:mockito/mockito.dart' as _i1; +import 'package:mockito/src/dummies.dart' as _i5; + +import 'api_service_test.dart' as _i4; + +// ignore_for_file: type=lint +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types +// ignore_for_file: subtype_of_sealed_class + +class _FakeMethodCodec_0 extends _i1.SmartFake implements _i2.MethodCodec { + _FakeMethodCodec_0( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeBinaryMessenger_1 extends _i1.SmartFake + implements _i2.BinaryMessenger { + _FakeBinaryMessenger_1( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeResponse_2 extends _i1.SmartFake implements _i3.Response { + _FakeResponse_2( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeStreamedResponse_3 extends _i1.SmartFake + implements _i3.StreamedResponse { + _FakeStreamedResponse_3( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +/// A class which mocks [MockMethodChannel]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockMockMethodChannel extends _i1.Mock implements _i4.MockMethodChannel { + MockMockMethodChannel() { + _i1.throwOnMissingStub(this); + } + + @override + String get name => (super.noSuchMethod( + Invocation.getter(#name), + returnValue: _i5.dummyValue( + this, + Invocation.getter(#name), + ), + ) as String); + + @override + _i2.MethodCodec get codec => (super.noSuchMethod( + Invocation.getter(#codec), + returnValue: _FakeMethodCodec_0( + this, + Invocation.getter(#codec), + ), + ) as _i2.MethodCodec); + + @override + _i2.BinaryMessenger get binaryMessenger => (super.noSuchMethod( + Invocation.getter(#binaryMessenger), + returnValue: _FakeBinaryMessenger_1( + this, + Invocation.getter(#binaryMessenger), + ), + ) as _i2.BinaryMessenger); + + @override + _i6.Future invokeMethod( + String? method, [ + dynamic arguments, + ]) => + (super.noSuchMethod( + Invocation.method( + #invokeMethod, + [ + method, + arguments, + ], + ), + returnValue: _i6.Future.value(), + ) as _i6.Future); + + @override + _i6.Future?> invokeListMethod( + String? method, [ + dynamic arguments, + ]) => + (super.noSuchMethod( + Invocation.method( + #invokeListMethod, + [ + method, + arguments, + ], + ), + returnValue: _i6.Future?>.value(), + ) as _i6.Future?>); + + @override + _i6.Future?> invokeMapMethod( + String? method, [ + dynamic arguments, + ]) => + (super.noSuchMethod( + Invocation.method( + #invokeMapMethod, + [ + method, + arguments, + ], + ), + returnValue: _i6.Future?>.value(), + ) as _i6.Future?>); + + @override + void setMethodCallHandler( + _i6.Future Function(_i2.MethodCall)? handler) => + super.noSuchMethod( + Invocation.method( + #setMethodCallHandler, + [handler], + ), + returnValueForMissingStub: null, + ); +} + +/// A class which mocks [Client]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockClient extends _i1.Mock implements _i3.Client { + MockClient() { + _i1.throwOnMissingStub(this); + } + + @override + _i6.Future<_i3.Response> head( + Uri? url, { + Map? headers, + }) => + (super.noSuchMethod( + Invocation.method( + #head, + [url], + {#headers: headers}, + ), + returnValue: _i6.Future<_i3.Response>.value(_FakeResponse_2( + this, + Invocation.method( + #head, + [url], + {#headers: headers}, + ), + )), + ) as _i6.Future<_i3.Response>); + + @override + _i6.Future<_i3.Response> get( + Uri? url, { + Map? headers, + }) => + (super.noSuchMethod( + Invocation.method( + #get, + [url], + {#headers: headers}, + ), + returnValue: _i6.Future<_i3.Response>.value(_FakeResponse_2( + this, + Invocation.method( + #get, + [url], + {#headers: headers}, + ), + )), + ) as _i6.Future<_i3.Response>); + + @override + _i6.Future<_i3.Response> post( + Uri? url, { + Map? headers, + Object? body, + _i7.Encoding? encoding, + }) => + (super.noSuchMethod( + Invocation.method( + #post, + [url], + { + #headers: headers, + #body: body, + #encoding: encoding, + }, + ), + returnValue: _i6.Future<_i3.Response>.value(_FakeResponse_2( + this, + Invocation.method( + #post, + [url], + { + #headers: headers, + #body: body, + #encoding: encoding, + }, + ), + )), + ) as _i6.Future<_i3.Response>); + + @override + _i6.Future<_i3.Response> put( + Uri? url, { + Map? headers, + Object? body, + _i7.Encoding? encoding, + }) => + (super.noSuchMethod( + Invocation.method( + #put, + [url], + { + #headers: headers, + #body: body, + #encoding: encoding, + }, + ), + returnValue: _i6.Future<_i3.Response>.value(_FakeResponse_2( + this, + Invocation.method( + #put, + [url], + { + #headers: headers, + #body: body, + #encoding: encoding, + }, + ), + )), + ) as _i6.Future<_i3.Response>); + + @override + _i6.Future<_i3.Response> patch( + Uri? url, { + Map? headers, + Object? body, + _i7.Encoding? encoding, + }) => + (super.noSuchMethod( + Invocation.method( + #patch, + [url], + { + #headers: headers, + #body: body, + #encoding: encoding, + }, + ), + returnValue: _i6.Future<_i3.Response>.value(_FakeResponse_2( + this, + Invocation.method( + #patch, + [url], + { + #headers: headers, + #body: body, + #encoding: encoding, + }, + ), + )), + ) as _i6.Future<_i3.Response>); + + @override + _i6.Future<_i3.Response> delete( + Uri? url, { + Map? headers, + Object? body, + _i7.Encoding? encoding, + }) => + (super.noSuchMethod( + Invocation.method( + #delete, + [url], + { + #headers: headers, + #body: body, + #encoding: encoding, + }, + ), + returnValue: _i6.Future<_i3.Response>.value(_FakeResponse_2( + this, + Invocation.method( + #delete, + [url], + { + #headers: headers, + #body: body, + #encoding: encoding, + }, + ), + )), + ) as _i6.Future<_i3.Response>); + + @override + _i6.Future read( + Uri? url, { + Map? headers, + }) => + (super.noSuchMethod( + Invocation.method( + #read, + [url], + {#headers: headers}, + ), + returnValue: _i6.Future.value(_i5.dummyValue( + this, + Invocation.method( + #read, + [url], + {#headers: headers}, + ), + )), + ) as _i6.Future); + + @override + _i6.Future<_i8.Uint8List> readBytes( + Uri? url, { + Map? headers, + }) => + (super.noSuchMethod( + Invocation.method( + #readBytes, + [url], + {#headers: headers}, + ), + returnValue: _i6.Future<_i8.Uint8List>.value(_i8.Uint8List(0)), + ) as _i6.Future<_i8.Uint8List>); + + @override + _i6.Future<_i3.StreamedResponse> send(_i3.BaseRequest? request) => + (super.noSuchMethod( + Invocation.method( + #send, + [request], + ), + returnValue: + _i6.Future<_i3.StreamedResponse>.value(_FakeStreamedResponse_3( + this, + Invocation.method( + #send, + [request], + ), + )), + ) as _i6.Future<_i3.StreamedResponse>); + + @override + void close() => super.noSuchMethod( + Invocation.method( + #close, + [], + ), + returnValueForMissingStub: null, + ); +} diff --git a/test/main_test.dart b/test/main_test.dart new file mode 100644 index 00000000..00fb9c1f --- /dev/null +++ b/test/main_test.dart @@ -0,0 +1,31 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:get/get.dart'; +import 'package:taskwarrior/app/routes/app_pages.dart'; + +void main() { + group('Main App Initialization', () { + testWidgets('App initializes with the correct route', + (WidgetTester tester) async { + await tester.pumpWidget(GetMaterialApp( + title: "Application", + initialRoute: AppPages.INITIAL, + getPages: AppPages.routes, + )); + + // Check if the app starts at the correct initial route + expect(Get.currentRoute, equals(AppPages.INITIAL)); + }); + + testWidgets('Splash screen displays the correct content', + (WidgetTester tester) async { + await tester.pumpWidget(GetMaterialApp( + title: "Application", + initialRoute: AppPages.INITIAL, + getPages: AppPages.routes, + )); + + // Check if the splash screen contains the expected text + expect(find.text("Setting up the app..."), findsOneWidget); + }); + }); +} diff --git a/test/utils/language/bengali_sentences_test.dart b/test/utils/language/bengali_sentences_test.dart index a9b86947..13a97ae4 100644 --- a/test/utils/language/bengali_sentences_test.dart +++ b/test/utils/language/bengali_sentences_test.dart @@ -104,7 +104,7 @@ void main() { expect(bengali.reportsPageAddTasksToSeeReports, 'রিপোর্ট দেখতে টাস্ক যোগ করুন'); expect(bengali.taskchampionTileDescription, - 'TaskWarrior সিঙ্কিং CCSync বা Taskchampion সিঙ্ক সার্ভারে পরিবর্তন করুন'); + 'Taskwarrior সিঙ্কিং CCSync বা Taskchampion সিঙ্ক সার্ভারে পরিবর্তন করুন'); expect(bengali.taskchampionTileTitle, 'Taskchampion সিঙ্ক'); expect(bengali.ccsyncCredentials, 'CCSync ক্রেডেনশিয়াল'); expect(bengali.deleteTaskConfirmation, 'টাস্ক মুছুন'); diff --git a/test/utils/language/english_sentences_test.dart b/test/utils/language/english_sentences_test.dart index d2ec4d5f..488e5413 100644 --- a/test/utils/language/english_sentences_test.dart +++ b/test/utils/language/english_sentences_test.dart @@ -44,7 +44,7 @@ void main() { 'Toggle between your native language'); expect(english.settingsPageSelectDirectoryTitle, 'Select the directory'); expect(english.settingsPageSelectDirectoryDescription, - 'Select the directory where the TaskWarrior data is stored\nCurrent directory: '); + 'Select the directory where the Taskwarrior data is stored\nCurrent directory: '); expect(english.settingsPageChangeDirectory, 'Change Directory'); expect(english.settingsPageSetToDefault, 'Set To Default'); expect(english.navDrawerProfile, 'Profile'); @@ -101,7 +101,7 @@ void main() { expect(english.reportsPageNoTasksFound, 'No Tasks Found'); expect(english.reportsPageAddTasksToSeeReports, 'Add Tasks To See Reports'); expect(english.taskchampionTileDescription, - 'Switch to TaskWarrior sync with CCSync or Taskchampion Sync Server'); + 'Switch to Taskwarrior sync with CCSync or Taskchampion Sync Server'); expect(english.taskchampionTileTitle, 'Taskchampion sync'); expect(english.ccsyncCredentials, 'CCync credentials'); expect(english.deleteTaskConfirmation, 'Delete Tasks'); diff --git a/test/utils/language/french_sentences_test.dart b/test/utils/language/french_sentences_test.dart index 1a271244..63afc0c0 100644 --- a/test/utils/language/french_sentences_test.dart +++ b/test/utils/language/french_sentences_test.dart @@ -46,7 +46,7 @@ void main() { 'Basculer entre les langues natives'); expect(french.settingsPageSelectDirectoryTitle, 'Choisir un répertoire'); expect(french.settingsPageSelectDirectoryDescription, - 'Choisissez le répertoire où les données TaskWarrior sont stockées\nRépertoire actuel : '); + 'Choisissez le répertoire où les données Taskwarrior sont stockées\nRépertoire actuel : '); expect(french.settingsPageChangeDirectory, 'Changer de répertoire'); expect(french.settingsPageSetToDefault, 'Définir par défaut'); expect(french.navDrawerProfile, 'Profil'); @@ -107,7 +107,7 @@ void main() { expect(french.reportsPageAddTasksToSeeReports, 'Ajoutez des tâches pour voir les rapports'); expect(french.taskchampionTileDescription, - 'Basculez la synchronisation de TaskWarrior vers le serveur de synchronisation CCSync ou Taskchampion'); + 'Basculez la synchronisation de Taskwarrior vers le serveur de synchronisation CCSync ou Taskchampion'); expect(french.taskchampionTileTitle, 'Synchronisation Taskchampion'); expect(french.ccsyncCredentials, 'Identifiants CCSync'); expect(french.deleteTaskConfirmation, 'Supprimer la tâche'); diff --git a/test/utils/language/hindi_sentences_test.dart b/test/utils/language/hindi_sentences_test.dart index ac5f5616..af8e2a2f 100644 --- a/test/utils/language/hindi_sentences_test.dart +++ b/test/utils/language/hindi_sentences_test.dart @@ -46,7 +46,7 @@ void main() { 'अपनी मातृभाषा के बीच टॉगल करें'); expect(hindi.settingsPageSelectDirectoryTitle, 'निर्देशिका चुनें'); expect(hindi.settingsPageSelectDirectoryDescription, - 'निर्देशिका चुनें जहां TaskWarrior डेटा स्टोर होता है\nवर्तमान निर्देशिका: '); + 'निर्देशिका चुनें जहां Taskwarrior डेटा स्टोर होता है\nवर्तमान निर्देशिका: '); expect(hindi.settingsPageChangeDirectory, 'निर्देशिका बदलें'); expect(hindi.settingsPageSetToDefault, 'डिफॉल्ट पर सेट करें'); expect(hindi.navDrawerProfile, 'प्रोफ़ाइल'); @@ -104,7 +104,7 @@ void main() { expect(hindi.reportsPageAddTasksToSeeReports, 'रिपोर्ट देखने के लिए कार्य जोड़ें'); expect(hindi.taskchampionTileDescription, - 'CCSync या Taskchampion सिंक सर्वर के साथ TaskWarrior सिंक पर स्विच करें'); + 'CCSync या Taskchampion सिंक सर्वर के साथ Taskwarrior सिंक पर स्विच करें'); expect(hindi.taskchampionTileTitle, 'Taskchampion सिंक'); expect(hindi.ccsyncCredentials, 'CCync क्रेडेन्शियल'); expect(hindi.deleteTaskConfirmation, 'कार्य हटाएं'); diff --git a/test/utils/language/marathi_sentences_test.dart b/test/utils/language/marathi_sentences_test.dart index 2e15cef8..8273bfac 100644 --- a/test/utils/language/marathi_sentences_test.dart +++ b/test/utils/language/marathi_sentences_test.dart @@ -45,7 +45,7 @@ void main() { 'तुमच्या मूल भाषेतर्फे टॉगल करा'); expect(marathi.settingsPageSelectDirectoryTitle, 'निर्देशिका निवडा'); expect(marathi.settingsPageSelectDirectoryDescription, - 'निर्देशिका निवडा जिथे TaskWarrior डेटा स्टोर केला जातो\nवर्तमान निर्देशिका: '); + 'निर्देशिका निवडा जिथे Taskwarrior डेटा स्टोर केला जातो\nवर्तमान निर्देशिका: '); expect(marathi.settingsPageChangeDirectory, 'डिरेक्टरी बदला'); expect(marathi.settingsPageSetToDefault, 'डीफॉल्टवर सेट करा'); expect(marathi.navDrawerProfile, 'प्रोफ़ाइल'); @@ -103,7 +103,7 @@ void main() { expect( marathi.reportsPageAddTasksToSeeReports, 'अहवाल पाहण्यासाठी काम जोडा'); expect(marathi.taskchampionTileDescription, - 'CCSync किंवा Taskchampion Sync Server सह TaskWarrior सिंक वर स्विच करा'); + 'CCSync किंवा Taskchampion Sync Server सह Taskwarrior सिंक वर स्विच करा'); expect(marathi.taskchampionTileTitle, 'Taskchampion सिंक'); expect(marathi.ccsyncCredentials, 'CCync क्रेडेन्शियल'); expect(marathi.deleteTaskConfirmation, 'कार्य हटवा'); diff --git a/test/utils/language/spanish_sentences_test.dart b/test/utils/language/spanish_sentences_test.dart index 45ccd8ff..df8d21ea 100644 --- a/test/utils/language/spanish_sentences_test.dart +++ b/test/utils/language/spanish_sentences_test.dart @@ -48,7 +48,7 @@ void main() { 'Alternar entre idiomas nativos'); expect(spanish.settingsPageSelectDirectoryTitle, 'Seleccionar directorio'); expect(spanish.settingsPageSelectDirectoryDescription, - 'Selecciona el directorio donde se almacenan los datos de TaskWarrior\nDirectorio actual: '); + 'Selecciona el directorio donde se almacenan los datos de Taskwarrior\nDirectorio actual: '); expect(spanish.settingsPageChangeDirectory, 'Cambiar directorio'); expect(spanish.settingsPageSetToDefault, 'Restablecer a predeterminado'); expect(spanish.navDrawerProfile, 'Perfil'); @@ -106,7 +106,7 @@ void main() { expect(spanish.reportsPageAddTasksToSeeReports, 'Agrega tareas para ver informes'); expect(spanish.taskchampionTileDescription, - 'Cambia la sincronización de TaskWarrior al servidor de sincronización CCSync o Taskchampion'); + 'Cambia la sincronización de Taskwarrior al servidor de sincronización CCSync o Taskchampion'); expect(spanish.taskchampionTileTitle, 'Sincronización Taskchampion'); expect(spanish.ccsyncCredentials, 'Credenciales de CCSync'); expect(spanish.deleteTaskConfirmation, 'Eliminar tarea');