Skip to content

Commit

Permalink
feat: implement break series selection and speed display (#89) (#469)
Browse files Browse the repository at this point in the history
  • Loading branch information
Grodien authored Dec 18, 2024
1 parent 50bedbb commit cd84c85
Show file tree
Hide file tree
Showing 101 changed files with 1,980 additions and 182 deletions.
4 changes: 4 additions & 0 deletions das_client/assets/icons/icon_indicator_checked.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
169 changes: 163 additions & 6 deletions das_client/integration_test/test/train_journey_table_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,159 @@ import 'package:das_client/app/pages/journey/train_journey/widgets/table/curve_p
import 'package:das_client/app/pages/journey/train_journey/widgets/table/protection_section_row.dart';
import 'package:das_client/app/pages/journey/train_journey/widgets/table/service_point_row.dart';
import 'package:das_client/app/pages/journey/train_journey/widgets/table/signal_row.dart';
import 'package:das_client/app/pages/journey/train_journey/widgets/train_journey.dart';
import 'package:das_client/app/pages/profile/profile_page.dart';
import 'package:das_client/app/widgets/table/das_table.dart';
import 'package:design_system_flutter/design_system_flutter.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:sbb_design_system_mobile/sbb_design_system_mobile.dart';

import '../app_test.dart';
import '../util/test_utils.dart';

void main() {
group('train journey table test', () {
testWidgets('test breaking series defaults to ??', (tester) async {
await prepareAndStartApp(tester);

// load train journey by filling out train selection page
await _loadTrainJourney(tester, trainNumber: '4816');

final breakingSeriesHeaderCell = find.byKey(TrainJourney.breakingSeriesHeaderKey);
expect(breakingSeriesHeaderCell, findsOneWidget);
expect(find.descendant(of: breakingSeriesHeaderCell, matching: find.text('??')), findsNWidgets(1));
});

testWidgets('test default breaking series is taken from train characteristics (R115)', (tester) async {
await prepareAndStartApp(tester);

// load train journey by filling out train selection page
await _loadTrainJourney(tester, trainNumber: 'T5');

final breakingSeriesHeaderCell = find.byKey(TrainJourney.breakingSeriesHeaderKey);
expect(breakingSeriesHeaderCell, findsOneWidget);
expect(find.descendant(of: breakingSeriesHeaderCell, matching: find.text('R115')), findsNWidgets(1));
});

testWidgets('test all breakseries options are displayed', (tester) async {
await prepareAndStartApp(tester);

// load train journey by filling out train selection page
await _loadTrainJourney(tester, trainNumber: 'T5');

// Open break series bottom sheet
await tapElement(tester, find.byKey(TrainJourney.breakingSeriesHeaderKey));

final expectedCategories = {'R', 'A', 'D'};

for (final entry in expectedCategories) {
expect(find.text(entry), findsOneWidget);
}

final expectedOptions = {
'R105',
'R115',
'R125',
'R135',
'R150',
'A50',
'A60',
'A65',
'A70',
'A75',
'A80',
'A85',
'A95',
'A105',
'A115',
'D30'
};

for (final entry in expectedOptions) {
expect(find.text(entry), findsAtLeast(1));
}
});

testWidgets('test message when no breakseries are defined', (tester) async {
await prepareAndStartApp(tester);

// load train journey by filling out train selection page
await _loadTrainJourney(tester, trainNumber: '7839');

// Open break series bottom sheet
await tapElement(tester, find.byKey(TrainJourney.breakingSeriesHeaderKey));

expect(find.text(l10n.p_train_journey_break_series_empty), findsOneWidget);
});

testWidgets('test speed values of default breakSeries (R115)', (tester) async {
await prepareAndStartApp(tester);

// load train journey by filling out train selection page
await _loadTrainJourney(tester, trainNumber: 'T5');

final expectedSpeeds = {
'Genève-Aéroport': '60',
'65.3': '44', // 1. Curve
'New Line Speed All': '60',
'Genève': '60',
'New Line Speed A Missing': '60',
'42.5': '44', // 2. Curve
'40.5': null, // 3. Curve
'Gland': '60',
};

for (final entry in expectedSpeeds.entries) {
final tableRow = findDASTableRowByText(entry.key);
expect(tableRow, findsOneWidget);

if (entry.value != null) {
final speedText = find.descendant(of: tableRow, matching: find.text(entry.value!));
expect(speedText, findsOneWidget);
} else {
final textWidgets = find.descendant(of: tableRow, matching: find.byWidgetPredicate((it) => it is Text));
expect(textWidgets, findsNWidgets(2)); // KM and Kurve text widgets
}
}
});

testWidgets('test speed values of missing break Series', (tester) async {
await prepareAndStartApp(tester);

// load train journey by filling out train selection page
await _loadTrainJourney(tester, trainNumber: 'T5');

await _selectBreakSeries(tester, breakSeries: 'A85');

final breakingSeriesHeaderCell = find.byKey(TrainJourney.breakingSeriesHeaderKey);
expect(breakingSeriesHeaderCell, findsOneWidget);
expect(find.descendant(of: breakingSeriesHeaderCell, matching: find.text('A85')), findsNWidgets(1));

final expectedSpeeds = {
'Genève-Aéroport': '90',
'65.3': '55', // 1. Curve
'New Line Speed All': '90',
'Genève': 'XX',
'New Line Speed A Missing': 'XX',
'42.5': 'XX', // 2. Curve
'40.5': null, // 3. Curve
'Gland': '90',
};

for (final entry in expectedSpeeds.entries) {
final tableRow = findDASTableRowByText(entry.key);
expect(tableRow, findsOneWidget);

if (entry.value != null) {
final speedText = find.descendant(of: tableRow, matching: find.text(entry.value!));
expect(speedText, findsOneWidget);
} else {
final textWidgets = find.descendant(of: tableRow, matching: find.byWidgetPredicate((it) => it is Text));
expect(textWidgets, findsNWidgets(2)); // KM and Kurve text widgets
}
}
});

testWidgets('test connection track is displayed correctly', (tester) async {
await prepareAndStartApp(tester);

Expand Down Expand Up @@ -131,7 +273,6 @@ void main() {
l10n.p_train_journey_table_journey_information_label,
l10n.p_train_journey_table_time_label,
l10n.p_train_journey_table_advised_speed_label,
l10n.p_train_journey_table_braked_weight_speed_label,
l10n.p_train_journey_table_graduated_speed_label,
];

Expand Down Expand Up @@ -437,7 +578,8 @@ void main() {
final rowsAtKm33_8 = findDASTableRowByText('33.8');
expect(rowsAtKm33_8, findsExactly(2));
final segment1CABStop = rowsAtKm33_8.last; // end should be after other elements at same location
final segment1CABStopIcon = find.descendant(of: segment1CABStop, matching: find.byKey(CABSignalingRow.cabSignalingEndIconKey));
final segment1CABStopIcon =
find.descendant(of: segment1CABStop, matching: find.byKey(CABSignalingRow.cabSignalingEndIconKey));
expect(segment1CABStopIcon, findsOneWidget);

// Track equipment segment without ETCS level 2 should be ignored
Expand All @@ -449,7 +591,8 @@ void main() {
final rowsAtKm12_5 = findDASTableRowByText('12.5');
expect(rowsAtKm12_5, findsExactly(2));
final segment2CABStart = rowsAtKm12_5.first; // start should be before other elements at same location
final segment2CABStartIcon = find.descendant(of: segment2CABStart, matching: find.byKey(CABSignalingRow.cabSignalingStartIconKey));
final segment2CABStartIcon =
find.descendant(of: segment2CABStart, matching: find.byKey(CABSignalingRow.cabSignalingStartIconKey));
expect(segment2CABStartIcon, findsOneWidget);
await tester.dragUntilVisible(find.text('75.3'), scrollableFinder, const Offset(0, -50));
final trackEquipmentTypeChange = findDASTableRowByText('56.8');
Expand All @@ -458,13 +601,15 @@ void main() {
final rothristServicePointRow = findDASTableRowByText('46.2');
expect(rothristServicePointRow, findsOneWidget); // no CAB signaling at connecting ETCS L2 segments
final segment2CABEnd = findDASTableRowByText('39.9');
final segment2CABEndIcon = find.descendant(of: segment2CABEnd, matching: find.byKey(CABSignalingRow.cabSignalingEndIconKey));
final segment2CABEndIcon =
find.descendant(of: segment2CABEnd, matching: find.byKey(CABSignalingRow.cabSignalingEndIconKey));
expect(segment2CABEndIcon, findsOneWidget);

// CAB segment with end outside train journey and start at 8.3 km
await tester.dragUntilVisible(find.text('9.5'), scrollableFinder, const Offset(0, -50));
final segment3CABStart = findDASTableRowByText('8.3');
final segment3CABStartIcon = find.descendant(of: segment3CABStart, matching: find.byKey(CABSignalingRow.cabSignalingStartIconKey));
final segment3CABStartIcon =
find.descendant(of: segment3CABStart, matching: find.byKey(CABSignalingRow.cabSignalingStartIconKey));
expect(segment3CABStartIcon, findsOneWidget);
});
});
Expand All @@ -487,3 +632,15 @@ Future<void> _loadTrainJourney(WidgetTester tester, {required String trainNumber
// wait for train journey to load
await tester.pumpAndSettle();
}

Future<void> _selectBreakSeries(WidgetTester tester, {required String breakSeries}) async {
// Open break series bottom sheet
await tapElement(tester, find.byKey(TrainJourney.breakingSeriesHeaderKey));

// Check if the bottom sheeet is opened
expect(find.text(l10n.p_train_journey_break_series), findsOneWidget);
await tapElement(tester, find.text(breakSeries));

// confirm button
await tapElement(tester, find.text(l10n.c_button_confirm));
}
2 changes: 1 addition & 1 deletion das_client/integration_test/test/train_journey_test.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import 'package:das_client/app/pages/journey/train_journey/widgets/header/header.dart';
import 'package:design_system_flutter/design_system_flutter.dart';
import 'package:sbb_design_system_mobile/sbb_design_system_mobile.dart';
import 'package:flutter_test/flutter_test.dart';

import '../app_test.dart';
Expand Down
2 changes: 1 addition & 1 deletion das_client/integration_test/test/train_search_test.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import 'package:das_client/util/error_code.dart';
import 'package:das_client/util/format.dart';
import 'package:design_system_flutter/design_system_flutter.dart';
import 'package:sbb_design_system_mobile/sbb_design_system_mobile.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

Expand Down
2 changes: 1 addition & 1 deletion das_client/integration_test/util/test_utils.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import 'package:das_client/app/widgets/table/das_table.dart';
import 'package:design_system_flutter/design_system_flutter.dart';
import 'package:sbb_design_system_mobile/sbb_design_system_mobile.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

Expand Down
7 changes: 4 additions & 3 deletions das_client/l10n/strings_de.arb
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
{
"c_app_name": "DAS Client",
"p_train_selection_load": "Übernehmen",
"p_train_selection_trainnumber_description": "Zugnummer",
"p_train_selection_ru_description": "EVU",
"p_train_selection_date_description": "Datum",
Expand All @@ -13,7 +12,8 @@
"p_train_journey_table_journey_information_label": "Streckeninformationen",
"p_train_journey_table_advised_speed_label": "FE",
"p_train_journey_table_graduated_speed_label": "OG",
"p_train_journey_table_braked_weight_speed_label": "R150",
"p_train_journey_break_series": "Bremsreihe",
"p_train_journey_break_series_empty": "Es sind keine Bremsreihen vorhanden",
"p_train_journey_table_curve_type_curve": "Kurve",
"p_train_journey_table_curve_type_station_exit_curve": "Kurve Ausfahrt",
"p_train_journey_table_curve_type_curve_after_halt": "Kurve nach Haltestelle",
Expand Down Expand Up @@ -44,5 +44,6 @@
"c_error_sfera_request_timeout": "Timeout bei der Anfrage",
"c_error_sfera_jp_unavailable": "Fahrordnung nicht vorhanden",
"c_error_sfera_sp_invalid": "Unvollständige Daten erhalten",
"c_connection_track_weiche": "Weiche"
"c_connection_track_weiche": "Weiche",
"c_button_confirm": "Übernehmen"
}
2 changes: 1 addition & 1 deletion das_client/lib/app.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import 'package:das_client/app/i18n/i18n.dart';
import 'package:das_client/app/nav/app_router.dart';
import 'package:design_system_flutter/design_system_flutter.dart';
import 'package:sbb_design_system_mobile/sbb_design_system_mobile.dart';
import 'package:flutter/material.dart';

class App extends StatelessWidget {
Expand Down
18 changes: 17 additions & 1 deletion das_client/lib/app/bloc/train_journey_cubit.dart
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import 'dart:async';

import 'package:das_client/app/model/ru.dart';
import 'package:das_client/app/model/train_journey_settings.dart';
import 'package:das_client/model/journey/break_series.dart';
import 'package:das_client/model/journey/journey.dart';
import 'package:das_client/sfera/sfera_component.dart';
import 'package:das_client/util/error_code.dart';
import 'package:fimber/fimber.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:rxdart/rxdart.dart';

part 'train_journey_state.dart';

Expand All @@ -20,11 +23,16 @@ class TrainJourneyCubit extends Cubit<TrainJourneyState> {

Stream<Journey?> get journeyStream => _sferaService.journeyStream;

final _settingsSubject = BehaviorSubject<TrainJourneySettings>.seeded(TrainJourneySettings());

Stream<TrainJourneySettings> get settingsStream => _settingsSubject.stream;

StreamSubscription? _stateSubscription;

void loadTrainJourney() async {
final currentState = state;
if (currentState is SelectingTrainJourneyState) {
_resetSettings();
final date = currentState.date;
final ru = currentState.ru;
final trainNumber = currentState.trainNumber;
Expand All @@ -43,7 +51,7 @@ class TrainJourneyCubit extends Cubit<TrainJourneyState> {
case SferaServiceState.connecting:
case SferaServiceState.handshaking:
case SferaServiceState.loadingJourney:
case SferaServiceState.loadingSegments:
case SferaServiceState.loadingAdditionalData:
emit(ConnectingState(ru, trainNumber, date));
break;
case SferaServiceState.disconnected:
Expand All @@ -57,6 +65,10 @@ class TrainJourneyCubit extends Cubit<TrainJourneyState> {
}
}

void _resetSettings() {
_settingsSubject.add(TrainJourneySettings());
}

void updateTrainNumber(String? trainNumber) {
if (state is SelectingTrainJourneyState) {
emit(SelectingTrainJourneyState(
Expand Down Expand Up @@ -101,6 +113,10 @@ class TrainJourneyCubit extends Cubit<TrainJourneyState> {
_stateSubscription?.cancel();
_stateSubscription = null;
}

void updateBreakSeries(BreakSeries selectedBreakSeries) {
_settingsSubject.add(_settingsSubject.value.copyWith(selectedBreakSeries: selectedBreakSeries));
}
}

extension ContextBlocExtension on BuildContext {
Expand Down
15 changes: 15 additions & 0 deletions das_client/lib/app/model/train_journey_settings.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import 'package:das_client/model/journey/break_series.dart';

class TrainJourneySettings {
TrainJourneySettings({
this.selectedBreakSeries,
});

final BreakSeries? selectedBreakSeries;

TrainJourneySettings copyWith({BreakSeries? selectedBreakSeries}) {
return TrainJourneySettings(
selectedBreakSeries: selectedBreakSeries ?? this.selectedBreakSeries,
);
}
}
2 changes: 1 addition & 1 deletion das_client/lib/app/nav/das_navigation_drawer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import 'package:das_client/app/i18n/i18n.dart';
import 'package:das_client/app/nav/app_router.dart';
import 'package:das_client/app/widgets/app_version_text.dart';
import 'package:das_client/app/widgets/device_id_text.dart';
import 'package:design_system_flutter/design_system_flutter.dart';
import 'package:sbb_design_system_mobile/sbb_design_system_mobile.dart';
import 'package:flutter/material.dart';

class DASNavigationDrawer extends StatelessWidget {
Expand Down
2 changes: 1 addition & 1 deletion das_client/lib/app/pages/journey/journey_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import 'package:das_client/app/pages/journey/train_journey/train_journey_overvie
import 'package:das_client/app/pages/journey/train_selection/train_selection.dart';
import 'package:das_client/auth/authentication_component.dart';
import 'package:das_client/di.dart';
import 'package:design_system_flutter/design_system_flutter.dart';
import 'package:sbb_design_system_mobile/sbb_design_system_mobile.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import 'package:das_client/app/pages/journey/train_journey/widgets/header/adl_notification.dart';
import 'package:das_client/app/pages/journey/train_journey/widgets/header/header.dart';
import 'package:das_client/app/pages/journey/train_journey/widgets/train_journey.dart';
import 'package:design_system_flutter/design_system_flutter.dart';
import 'package:sbb_design_system_mobile/sbb_design_system_mobile.dart';
import 'package:flutter/material.dart';

// TODO: handle extraLarge font sizes (diff to figma) globally.
Expand Down
Loading

0 comments on commit cd84c85

Please sign in to comment.