add traction import export
All checks were successful
Release / meta (push) Successful in 3s
Release / linux-build (push) Successful in 1m1s
Release / web-build (push) Successful in 1m22s
Release / android-build (push) Successful in 6m49s
Release / release-master (push) Successful in 5s
Release / release-dev (push) Successful in 8s
All checks were successful
Release / meta (push) Successful in 3s
Release / linux-build (push) Successful in 1m1s
Release / web-build (push) Successful in 1m22s
Release / android-build (push) Successful in 6m49s
Release / release-master (push) Successful in 5s
Release / release-dev (push) Successful in 8s
This commit is contained in:
265
test/helpers/fake_services.dart
Normal file
265
test/helpers/fake_services.dart
Normal file
@@ -0,0 +1,265 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:mileograph_flutter/objects/objects.dart';
|
||||
import 'package:mileograph_flutter/services/api_service.dart';
|
||||
import 'package:mileograph_flutter/services/authservice.dart';
|
||||
import 'package:mileograph_flutter/services/data_service.dart';
|
||||
import 'package:mileograph_flutter/services/distance_unit_service.dart';
|
||||
|
||||
import 'test_data.dart';
|
||||
|
||||
class FakeApiService extends ApiService {
|
||||
FakeApiService({String baseUrl = 'https://example.com'})
|
||||
: super(baseUrl: baseUrl);
|
||||
|
||||
final Map<String, dynamic> getResponses = {};
|
||||
final Map<String, dynamic> postResponses = {};
|
||||
|
||||
@override
|
||||
Future<dynamic> get(String endpoint, {Map<String, String>? headers}) async {
|
||||
return getResponses[endpoint];
|
||||
}
|
||||
|
||||
@override
|
||||
Future<dynamic> post(
|
||||
String endpoint,
|
||||
dynamic data, {
|
||||
Map<String, String>? headers,
|
||||
}) async {
|
||||
return postResponses[endpoint] ?? {};
|
||||
}
|
||||
}
|
||||
|
||||
class FakeAuthService extends AuthService {
|
||||
FakeAuthService({
|
||||
required super.api,
|
||||
this.userIdValue = 'user-123',
|
||||
this.usernameValue = 'railfan',
|
||||
this.fullNameValue = 'Alex Rider',
|
||||
this.isElevatedValue = true,
|
||||
this.isLoggedInValue = true,
|
||||
});
|
||||
|
||||
final String? userIdValue;
|
||||
final String? usernameValue;
|
||||
final String? fullNameValue;
|
||||
final bool isElevatedValue;
|
||||
final bool isLoggedInValue;
|
||||
|
||||
@override
|
||||
bool get isLoggedIn => isLoggedInValue;
|
||||
|
||||
@override
|
||||
String? get userId => userIdValue;
|
||||
|
||||
@override
|
||||
String? get username => usernameValue;
|
||||
|
||||
@override
|
||||
String? get fullName => fullNameValue;
|
||||
|
||||
@override
|
||||
bool get isElevated => isElevatedValue;
|
||||
}
|
||||
|
||||
class FakeDataService extends DataService {
|
||||
FakeDataService({ApiService? api})
|
||||
: super(api: api ?? FakeApiService());
|
||||
|
||||
HomepageStats? homepageStatsValue;
|
||||
bool isHomepageLoadingValue = false;
|
||||
List<TripSummary> tripsValue = [];
|
||||
List<Leg> onThisDayValue = [];
|
||||
bool isOnThisDayLoadingValue = false;
|
||||
List<ClassClearanceProgress> classClearanceProgressValue = [];
|
||||
bool isClassClearanceProgressLoadingValue = false;
|
||||
List<LocoSummary> tractionValue = [];
|
||||
bool isTractionLoadingValue = false;
|
||||
bool tractionHasMoreValue = false;
|
||||
List<LocoChange> latestLocoChangesValue = [];
|
||||
bool isLatestLocoChangesLoadingValue = false;
|
||||
bool latestLocoChangesHasMoreValue = false;
|
||||
List<Leg> legsValue = [];
|
||||
bool isLegsLoadingValue = false;
|
||||
bool legsHasMoreValue = false;
|
||||
List<TripDetail> tripDetailsValue = [];
|
||||
List<TripSummary> tripListValue = [];
|
||||
bool isTripDetailsLoadingValue = false;
|
||||
List<String> locoClassesValue = [];
|
||||
List<EventField> eventFieldsValue = [];
|
||||
bool isEventFieldsLoadingValue = false;
|
||||
int pendingLocoCountValue = 0;
|
||||
double currentYearMileageValue = 0;
|
||||
|
||||
@override
|
||||
HomepageStats? get homepageStats => homepageStatsValue;
|
||||
|
||||
@override
|
||||
bool get isHomepageLoading => isHomepageLoadingValue;
|
||||
|
||||
@override
|
||||
List<TripSummary> get trips => tripsValue;
|
||||
|
||||
@override
|
||||
List<Leg> get onThisDay => onThisDayValue;
|
||||
|
||||
@override
|
||||
bool get isOnThisDayLoading => isOnThisDayLoadingValue;
|
||||
|
||||
@override
|
||||
List<ClassClearanceProgress> get classClearanceProgress =>
|
||||
classClearanceProgressValue;
|
||||
|
||||
@override
|
||||
bool get isClassClearanceProgressLoading =>
|
||||
isClassClearanceProgressLoadingValue;
|
||||
|
||||
@override
|
||||
List<LocoSummary> get traction => tractionValue;
|
||||
|
||||
@override
|
||||
bool get isTractionLoading => isTractionLoadingValue;
|
||||
|
||||
@override
|
||||
bool get tractionHasMore => tractionHasMoreValue;
|
||||
|
||||
@override
|
||||
List<LocoChange> get latestLocoChanges => latestLocoChangesValue;
|
||||
|
||||
@override
|
||||
bool get isLatestLocoChangesLoading => isLatestLocoChangesLoadingValue;
|
||||
|
||||
@override
|
||||
bool get latestLocoChangesHasMore => latestLocoChangesHasMoreValue;
|
||||
|
||||
@override
|
||||
List<Leg> get legs => legsValue;
|
||||
|
||||
@override
|
||||
bool get isLegsLoading => isLegsLoadingValue;
|
||||
|
||||
@override
|
||||
bool get legsHasMore => legsHasMoreValue;
|
||||
|
||||
@override
|
||||
List<TripDetail> get tripDetails => tripDetailsValue;
|
||||
|
||||
@override
|
||||
List<TripSummary> get tripList => tripListValue;
|
||||
|
||||
@override
|
||||
bool get isTripDetailsLoading => isTripDetailsLoadingValue;
|
||||
|
||||
@override
|
||||
List<String> get locoClasses => locoClassesValue;
|
||||
|
||||
@override
|
||||
List<EventField> get eventFields => eventFieldsValue;
|
||||
|
||||
@override
|
||||
bool get isEventFieldsLoading => isEventFieldsLoadingValue;
|
||||
|
||||
@override
|
||||
int get pendingLocoCount => pendingLocoCountValue;
|
||||
|
||||
@override
|
||||
double getMileageForCurrentYear() => currentYearMileageValue;
|
||||
|
||||
@override
|
||||
Future<void> fetchHomepageStats() async {}
|
||||
|
||||
@override
|
||||
Future<void> fetchOnThisDay({DateTime? date}) async {}
|
||||
|
||||
@override
|
||||
Future<void> fetchTripDetails() async {}
|
||||
|
||||
@override
|
||||
Future<void> fetchHadTraction({int offset = 0, int limit = 100}) async {}
|
||||
|
||||
@override
|
||||
Future<void> fetchLatestLocoChanges({
|
||||
int limit = 100,
|
||||
int offset = 0,
|
||||
bool append = false,
|
||||
}) async {}
|
||||
|
||||
@override
|
||||
Future<void> fetchClassClearanceProgress({
|
||||
int offset = 0,
|
||||
int limit = 20,
|
||||
bool append = false,
|
||||
bool onlyIncomplete = false,
|
||||
}) async {}
|
||||
|
||||
@override
|
||||
Future<void> fetchLegs({
|
||||
int offset = 0,
|
||||
int limit = 100,
|
||||
String sortBy = 'date',
|
||||
int sortDirection = 0,
|
||||
String? dateRangeStart,
|
||||
String? dateRangeEnd,
|
||||
bool append = false,
|
||||
bool unallocatedOnly = false,
|
||||
}) async {}
|
||||
|
||||
@override
|
||||
Future<List<TripLocoStat>> fetchTripLocoStats(int tripId) async {
|
||||
return const [];
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> fetchTraction({
|
||||
bool hadOnly = false,
|
||||
int offset = 0,
|
||||
int limit = 100,
|
||||
String? locoClass,
|
||||
String? locoNumber,
|
||||
bool mileageFirst = true,
|
||||
bool append = false,
|
||||
Map<String, dynamic>? filters,
|
||||
}) async {}
|
||||
|
||||
@override
|
||||
Future<List<String>> fetchClassList({bool force = false}) async {
|
||||
return locoClassesValue;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> fetchEventFields({bool force = false}) async {}
|
||||
|
||||
@override
|
||||
Future<void> fetchPendingLocoCount() async {}
|
||||
|
||||
@override
|
||||
Future<Map<String, dynamic>?> fetchClassStats(String locoClass) async {
|
||||
return TestData.classStats;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<LeaderboardEntry>> fetchClassLeaderboard(
|
||||
String locoClass, {
|
||||
bool friends = false,
|
||||
}) async {
|
||||
return TestData.classLeaderboard;
|
||||
}
|
||||
}
|
||||
|
||||
class FakeDistanceUnitService extends DistanceUnitService {
|
||||
FakeDistanceUnitService({this.unitOverride});
|
||||
|
||||
final DistanceUnit? unitOverride;
|
||||
|
||||
@override
|
||||
DistanceUnit get unit => unitOverride ?? super.unit;
|
||||
|
||||
@override
|
||||
String format(
|
||||
double miles, {
|
||||
int decimals = 1,
|
||||
bool includeUnit = true,
|
||||
}) {
|
||||
final formatter = DistanceFormatter(unitOverride ?? super.unit);
|
||||
return formatter.format(miles, decimals: decimals, includeUnit: includeUnit);
|
||||
}
|
||||
}
|
||||
42
test/helpers/test_app.dart
Normal file
42
test/helpers/test_app.dart
Normal file
@@ -0,0 +1,42 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:mileograph_flutter/services/authservice.dart';
|
||||
import 'package:mileograph_flutter/services/data_service.dart';
|
||||
import 'package:mileograph_flutter/services/distance_unit_service.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
Widget buildTestApp({
|
||||
required Widget child,
|
||||
required DataService dataService,
|
||||
required AuthService authService,
|
||||
DistanceUnitService? distanceUnitService,
|
||||
}) {
|
||||
return MultiProvider(
|
||||
providers: [
|
||||
ChangeNotifierProvider<AuthService>.value(value: authService),
|
||||
ChangeNotifierProvider<DataService>.value(value: dataService),
|
||||
ChangeNotifierProvider<DistanceUnitService>.value(
|
||||
value: distanceUnitService ?? DistanceUnitService(),
|
||||
),
|
||||
],
|
||||
child: MaterialApp(home: child),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildTestRouterApp({
|
||||
required GoRouter router,
|
||||
required DataService dataService,
|
||||
required AuthService authService,
|
||||
DistanceUnitService? distanceUnitService,
|
||||
}) {
|
||||
return MultiProvider(
|
||||
providers: [
|
||||
ChangeNotifierProvider<AuthService>.value(value: authService),
|
||||
ChangeNotifierProvider<DataService>.value(value: dataService),
|
||||
ChangeNotifierProvider<DistanceUnitService>.value(
|
||||
value: distanceUnitService ?? DistanceUnitService(),
|
||||
),
|
||||
],
|
||||
child: MaterialApp.router(routerConfig: router),
|
||||
);
|
||||
}
|
||||
328
test/helpers/test_data.dart
Normal file
328
test/helpers/test_data.dart
Normal file
@@ -0,0 +1,328 @@
|
||||
import 'package:mileograph_flutter/objects/objects.dart';
|
||||
|
||||
class TestData {
|
||||
static final user = UserData(
|
||||
username: 'railfan',
|
||||
fullName: 'Alex Rider',
|
||||
userId: 'user-123',
|
||||
email: 'alex@example.com',
|
||||
entriesVisibility: 'public',
|
||||
mileageVisibility: 'public',
|
||||
elevated: true,
|
||||
disabled: false,
|
||||
);
|
||||
|
||||
static final topLocos = [
|
||||
LocoSummary(
|
||||
locoId: 100,
|
||||
locoType: 'D',
|
||||
locoNumber: '001',
|
||||
locoName: 'Atlas',
|
||||
locoClass: '67',
|
||||
locoOperator: 'TestRail',
|
||||
mileage: 1200.5,
|
||||
journeys: 18,
|
||||
trips: 6,
|
||||
status: 'Active',
|
||||
domain: 'mainline',
|
||||
owner: 'TestRail',
|
||||
livery: 'Blue',
|
||||
location: 'Depot',
|
||||
),
|
||||
LocoSummary(
|
||||
locoId: 101,
|
||||
locoType: 'D',
|
||||
locoNumber: '002',
|
||||
locoName: 'Orion',
|
||||
locoClass: '68',
|
||||
locoOperator: 'TestRail',
|
||||
mileage: 980.2,
|
||||
journeys: 12,
|
||||
trips: 4,
|
||||
status: 'Active',
|
||||
domain: 'mainline',
|
||||
owner: 'TestRail',
|
||||
livery: 'Orange',
|
||||
location: 'Central',
|
||||
),
|
||||
];
|
||||
|
||||
static final leaderboard = [
|
||||
LeaderboardEntry(
|
||||
userId: 'u1',
|
||||
username: 'driver_one',
|
||||
userFullName: 'Driver One',
|
||||
mileage: 3456.7,
|
||||
),
|
||||
LeaderboardEntry(
|
||||
userId: 'u2',
|
||||
username: 'driver_two',
|
||||
userFullName: 'Driver Two',
|
||||
mileage: 2999.1,
|
||||
),
|
||||
];
|
||||
|
||||
static final tripSummaries = [
|
||||
TripSummary(
|
||||
tripId: 1,
|
||||
tripName: 'North Run',
|
||||
tripMileage: 220.4,
|
||||
legCount: 3,
|
||||
locoStats: [
|
||||
TripLocoStat(locoClass: '67', number: '001', won: true),
|
||||
TripLocoStat(locoClass: '68', number: '002', won: false),
|
||||
],
|
||||
startDate: DateTime(2023, 10, 11),
|
||||
endDate: DateTime(2023, 10, 13),
|
||||
),
|
||||
TripSummary(
|
||||
tripId: 2,
|
||||
tripName: 'Harbor Loop',
|
||||
tripMileage: 85.6,
|
||||
legCount: 1,
|
||||
locoStats: const [],
|
||||
startDate: DateTime(2023, 11, 5),
|
||||
endDate: DateTime(2023, 11, 5),
|
||||
),
|
||||
];
|
||||
|
||||
static final tripDetails = [
|
||||
TripDetail(
|
||||
id: 1,
|
||||
name: 'North Run',
|
||||
mileage: 220.4,
|
||||
legCount: 3,
|
||||
locoStats: [
|
||||
TripLocoStat(locoClass: '67', number: '001', won: true),
|
||||
],
|
||||
legs: [
|
||||
TripLeg(
|
||||
id: 10,
|
||||
start: 'London',
|
||||
end: 'Leeds',
|
||||
beginTime: DateTime(2023, 10, 11, 9, 15),
|
||||
network: 'National',
|
||||
route: 'London → Leeds',
|
||||
mileage: 185.2,
|
||||
notes: 'Clear run',
|
||||
locos: [
|
||||
Loco(
|
||||
id: 100,
|
||||
type: 'D',
|
||||
number: '001',
|
||||
name: 'Atlas',
|
||||
locoClass: '67',
|
||||
operator: 'TestRail',
|
||||
notes: '',
|
||||
evn: '',
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
];
|
||||
|
||||
static final legs = [
|
||||
Leg(
|
||||
id: 501,
|
||||
tripId: 1,
|
||||
start: 'London',
|
||||
end: 'Oxford',
|
||||
beginTime: DateTime(2023, 9, 12, 7, 45),
|
||||
timezone: 0,
|
||||
network: 'National',
|
||||
route: ['London', 'Oxford'],
|
||||
mileage: 62.3,
|
||||
notes: 'Morning service',
|
||||
headcode: '1A00',
|
||||
driving: 0,
|
||||
user: 'railfan',
|
||||
locos: [
|
||||
Loco(
|
||||
id: 100,
|
||||
type: 'D',
|
||||
number: '001',
|
||||
name: 'Atlas',
|
||||
locoClass: '67',
|
||||
operator: 'TestRail',
|
||||
notes: '',
|
||||
evn: '',
|
||||
),
|
||||
],
|
||||
),
|
||||
Leg(
|
||||
id: 502,
|
||||
tripId: 2,
|
||||
start: 'Oxford',
|
||||
end: 'Bristol',
|
||||
beginTime: DateTime(2023, 9, 13, 16, 10),
|
||||
timezone: 0,
|
||||
network: 'National',
|
||||
route: ['Oxford', 'Bristol'],
|
||||
mileage: 74.8,
|
||||
notes: 'Evening run',
|
||||
headcode: '1B10',
|
||||
driving: 0,
|
||||
user: 'railfan',
|
||||
locos: [
|
||||
Loco(
|
||||
id: 101,
|
||||
type: 'D',
|
||||
number: '002',
|
||||
name: 'Orion',
|
||||
locoClass: '68',
|
||||
operator: 'TestRail',
|
||||
notes: '',
|
||||
evn: '',
|
||||
),
|
||||
],
|
||||
),
|
||||
];
|
||||
|
||||
static List<Leg> onThisDayLegs() {
|
||||
final pastYear = DateTime.now().year - 1;
|
||||
return [
|
||||
Leg(
|
||||
id: 900,
|
||||
tripId: 3,
|
||||
start: 'York',
|
||||
end: 'Durham',
|
||||
beginTime: DateTime(pastYear, 7, 4, 10, 30),
|
||||
timezone: 0,
|
||||
network: 'National',
|
||||
route: ['York', 'Durham'],
|
||||
mileage: 60.0,
|
||||
notes: 'Anniversary run',
|
||||
headcode: '1C11',
|
||||
driving: 0,
|
||||
user: 'railfan',
|
||||
locos: [
|
||||
Loco(
|
||||
id: 102,
|
||||
type: 'D',
|
||||
number: '003',
|
||||
name: 'Nova',
|
||||
locoClass: '66',
|
||||
operator: 'TestRail',
|
||||
notes: '',
|
||||
evn: '',
|
||||
),
|
||||
],
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
static final latestLocoChanges = [
|
||||
LocoChange(
|
||||
locoId: 200,
|
||||
locoClass: '67',
|
||||
locoNumber: '001',
|
||||
locoName: 'Atlas',
|
||||
attrCode: 'loco_status',
|
||||
attrDisplay: 'Status',
|
||||
valueDisplay: 'Active',
|
||||
validFrom: DateTime(2023, 10, 1),
|
||||
approvedAt: DateTime(2023, 10, 2),
|
||||
approvedBy: 'moderator',
|
||||
),
|
||||
];
|
||||
|
||||
static final classClearance = [
|
||||
ClassClearanceProgress(
|
||||
className: 'Class 67',
|
||||
completed: 12,
|
||||
total: 30,
|
||||
percentComplete: 40.0,
|
||||
activeCompleted: 8,
|
||||
activeTotal: 20,
|
||||
activePercent: 40.0,
|
||||
),
|
||||
];
|
||||
|
||||
static final traction = [
|
||||
LocoSummary(
|
||||
locoId: 100,
|
||||
locoType: 'D',
|
||||
locoNumber: '001',
|
||||
locoName: 'Atlas',
|
||||
locoClass: '67',
|
||||
locoOperator: 'TestRail',
|
||||
mileage: 1200.5,
|
||||
journeys: 18,
|
||||
trips: 6,
|
||||
status: 'Active',
|
||||
domain: 'mainline',
|
||||
owner: 'TestRail',
|
||||
livery: 'Blue',
|
||||
location: 'Depot',
|
||||
),
|
||||
LocoSummary(
|
||||
locoId: 101,
|
||||
locoType: 'D',
|
||||
locoNumber: '002',
|
||||
locoName: 'Orion',
|
||||
locoClass: '68',
|
||||
locoOperator: 'TestRail',
|
||||
mileage: 980.2,
|
||||
journeys: 12,
|
||||
trips: 4,
|
||||
status: 'Active',
|
||||
domain: 'mainline',
|
||||
owner: 'TestRail',
|
||||
livery: 'Orange',
|
||||
location: 'Central',
|
||||
),
|
||||
];
|
||||
|
||||
static final eventFields = [
|
||||
const EventField(name: 'power_unit', display: 'Power Unit', type: 'text'),
|
||||
const EventField(
|
||||
name: 'cab_air_conditioning',
|
||||
display: 'Cab A/C',
|
||||
enumValues: ['Yes', 'No'],
|
||||
),
|
||||
];
|
||||
|
||||
static final classStats = <String, dynamic>{
|
||||
'loco_class': '67',
|
||||
'total_mileage_with_class': 5500.5,
|
||||
'avg_mileage_per_entry': 80.2,
|
||||
'avg_mileage_per_loco_had': 420.0,
|
||||
'had_count': 12,
|
||||
'entries_with_class': 42,
|
||||
'class_stats': {
|
||||
'total': 30,
|
||||
'status': [
|
||||
{'status': 'Active', 'count': 18},
|
||||
{'status': 'Stored', 'count': 12},
|
||||
],
|
||||
'domain': [
|
||||
{'domain': 'mainline', 'count': 20},
|
||||
{'domain': 'heritage', 'count': 10},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
static final classLeaderboard = [
|
||||
LeaderboardEntry(
|
||||
userId: 'u3',
|
||||
username: 'fan_three',
|
||||
userFullName: 'Fan Three',
|
||||
mileage: 1500.0,
|
||||
),
|
||||
];
|
||||
|
||||
static final homepageStats = HomepageStats(
|
||||
totalMileage: 4523.8,
|
||||
yearlyMileage: [
|
||||
YearlyMileage(year: DateTime.now().year, mileage: 1200.5),
|
||||
YearlyMileage(year: DateTime.now().year - 1, mileage: 990.2),
|
||||
],
|
||||
topLocos: topLocos,
|
||||
leaderboard: leaderboard,
|
||||
friendsLeaderboard: leaderboard,
|
||||
trips: tripSummaries,
|
||||
legCount: 27,
|
||||
user: user,
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user