Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 22 additions & 8 deletions waydowntown_app/lib/games/collector_game.dart
Original file line number Diff line number Diff line change
Expand Up @@ -327,8 +327,14 @@ class CollectorGameState extends State<CollectorGame>
List<_TimelineItem> _buildTimelineItems(BuildContext context) {
final items = <_TimelineItem>[];
final teamSubmissions = _visibleTeamSubmissions();
final teamSubmissionValues =
teamSubmissions.map((submission) => submission.submission).toSet();
final visibleDetectedValues = detectedItems
.where(
(item) => showIncorrectSubmissions || item.state != SubmissionState.incorrect)
.where((item) =>
item.state == SubmissionState.correct ||
item.state == SubmissionState.incorrect)
.map((item) => item.value)
.toSet();

for (final hint in hints) {
items.add(_TimelineItem(
Expand All @@ -341,11 +347,6 @@ class CollectorGameState extends State<CollectorGame>
if (!showIncorrectSubmissions && item.state == SubmissionState.incorrect) {
continue;
}
if ((item.state == SubmissionState.correct ||
item.state == SubmissionState.incorrect) &&
teamSubmissionValues.contains(item.value)) {
continue;
}

items.add(_TimelineItem(
timestamp: item.submittedAt,
Expand All @@ -354,13 +355,26 @@ class CollectorGameState extends State<CollectorGame>
}

for (final submission in teamSubmissions) {
if (visibleDetectedValues.contains(submission.submission)) {
continue;
}
items.add(_TimelineItem(
timestamp: submission.insertedAt,
widget: _buildSubmissionTile(submission),
));
}

items.sort((a, b) => b.timestamp.compareTo(a.timestamp));
final insertionOrder = <_TimelineItem, int>{
for (var i = 0; i < items.length; i++) items[i]: i,
};

items.sort((a, b) {
final timeCompare = b.timestamp.compareTo(a.timestamp);
if (timeCompare != 0) {
return timeCompare;
}
return insertionOrder[b]!.compareTo(insertionOrder[a]!);
});
return items;
}

Expand Down
2 changes: 1 addition & 1 deletion waydowntown_app/lib/games/food_court_frenzy.dart
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ class FoodCourtFrenzyGameState extends State<FoodCourtFrenzyGame>
try {
final answerData = await requestHint(answer.answer.id);
setState(() {
answer.hint = answerData?.label;
answer.hint = answerData?.hint;
answer.isLoadingHint = false;
});
} catch (e) {
Expand Down
9 changes: 5 additions & 4 deletions waydowntown_app/lib/models/answer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,13 @@ class Answer {
});

factory Answer.fromJson(Map<String, dynamic> json) {
final attributes = json['attributes'] as Map<String, dynamic>?;
return Answer(
id: json['id'],
label: json['attributes']['label'],
order: json['attributes']['order'],
hint: json['attributes']['hint'],
hasHint: json['attributes']['has_hint'],
label: attributes?['label'],
order: attributes?['order'],
hint: attributes?['hint'],
hasHint: attributes?['has_hint'] == true,
);
}
}
9 changes: 8 additions & 1 deletion waydowntown_app/lib/routes/request_run_route.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:phoenix_socket/phoenix_socket.dart';
import 'package:sentry/sentry.dart';
import 'package:waydowntown/app.dart';
import 'package:waydowntown/models/run.dart';
Expand All @@ -10,13 +11,15 @@ class RequestRunRoute extends StatefulWidget {
final String? concept;
final String? specificationId;
final String? position;
final PhoenixSocket? testSocket;

const RequestRunRoute({
super.key,
required this.dio,
this.concept,
this.specificationId,
this.position,
this.testSocket,
});

@override
Expand Down Expand Up @@ -91,7 +94,11 @@ class RequestRunRouteState extends State<RequestRunRoute> {
appBar: AppBar(title: const Text('Game')),
body: const Center(child: CircularProgressIndicator()));
} else {
return RunLaunchRoute(run: run!, dio: widget.dio);
return RunLaunchRoute(
run: run!,
dio: widget.dio,
testSocket: widget.testSocket,
);
}
}
}
5 changes: 5 additions & 0 deletions waydowntown_app/lib/widgets/edit_specification_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ class EditSpecificationWidgetState extends State<EditSpecificationWidget> {
const SizedBox(width: 8),
_buildSortButton(
context: context,
key: const Key('region-sort-alpha'),
icon: const Icon(Icons.sort_by_alpha),
isActive: !_sortByDistance,
onPressed: () {
Expand All @@ -209,13 +210,15 @@ class EditSpecificationWidgetState extends State<EditSpecificationWidget> {
const SizedBox(width: 8),
_buildSortButton(
context: context,
key: const Key('region-sort-nearest'),
icon: const Icon(Icons.near_me),
isActive: _sortByDistance,
onPressed: _loadNearestRegions,
),
const SizedBox(width: 8),
_buildSortButton(
context: context,
key: const Key('region-sort-add'),
icon: const Icon(Icons.add),
isActive: false,
onPressed: _createNewRegion,
Expand Down Expand Up @@ -407,11 +410,13 @@ class EditSpecificationWidgetState extends State<EditSpecificationWidget> {

Widget _buildSortButton({
required BuildContext context,
Key? key,
required Icon icon,
required bool isActive,
required VoidCallback onPressed,
}) {
return IconButton(
key: key,
onPressed: onPressed,
icon: icon,
style: IconButton.styleFrom(
Expand Down
3 changes: 2 additions & 1 deletion waydowntown_app/test/games/bluetooth_collector_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,10 @@ void main() {
when(device3.remoteId).thenReturn(const DeviceIdentifier("3"));
});

setUp(() {
setUp(() async {
mockFlutterBluePlus = MockFlutterBluePlusMockable();
dotenv.testLoad(fileInput: File('.env').readAsStringSync());
await TestHelpers.setMockUser();

dio = Dio(BaseOptions(baseUrl: dotenv.env['API_ROOT']!));
dio.interceptors.add(PrettyDioLogger());
Expand Down
3 changes: 2 additions & 1 deletion waydowntown_app/test/games/cardinal_memory_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,9 @@ void main() {

late MockPhoenixChannel mockChannel;

setUp(() {
setUp(() async {
mockMotionSensors = MockMotionSensors();
await TestHelpers.setMockUser();
dio = Dio(BaseOptions(baseUrl: dotenv.env['API_ROOT']!));
dio.interceptors.add(PrettyDioLogger());
dioAdapter = DioAdapter(dio: dio);
Expand Down
3 changes: 2 additions & 1 deletion waydowntown_app/test/games/code_collector_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,10 @@ void main() {

late MockMobileScannerController mockController;

setUp(() {
setUp(() async {
mockController = MockMobileScannerController();
dotenv.testLoad(fileInput: File('.env').readAsStringSync());
await TestHelpers.setMockUser();

dio = Dio(BaseOptions(baseUrl: dotenv.env['API_ROOT']!));
dio.interceptors.add(PrettyDioLogger());
Expand Down
3 changes: 2 additions & 1 deletion waydowntown_app/test/games/food_court_frenzy_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ void main() {
late Run run;
late MockPhoenixChannel mockChannel;

setUp(() {
setUp(() async {
await TestHelpers.setMockUser();
dio = Dio(BaseOptions(baseUrl: dotenv.env['API_ROOT']!));
dio.interceptors.add(PrettyDioLogger());
dioAdapter = DioAdapter(dio: dio);
Expand Down
3 changes: 2 additions & 1 deletion waydowntown_app/test/games/orientation_memory_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@ void main() {
late MockMotionSensors mockMotionSensors;
late MockPhoenixChannel mockChannel;

setUp(() {
setUp(() async {
mockMotionSensors = MockMotionSensors();
await TestHelpers.setMockUser();
dio = Dio(BaseOptions(baseUrl: dotenv.env['API_ROOT']!));
dio.interceptors.add(PrettyDioLogger());
dioAdapter = DioAdapter(dio: dio);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ void main() {
late Run run;
late MockPhoenixChannel mockChannel;

setUp(() {
setUp(() async {
await TestHelpers.setMockUser();
dio = Dio(BaseOptions(baseUrl: dotenv.env['API_ROOT']!));
dio.interceptors.add(PrettyDioLogger());
dioAdapter = DioAdapter(dio: dio);
Expand Down
3 changes: 2 additions & 1 deletion waydowntown_app/test/games/string_collector_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ void main() {
late Run game;
late MockPhoenixChannel mockChannel;

setUp(() {
setUp(() async {
await TestHelpers.setMockUser();
dio = Dio(BaseOptions(baseUrl: dotenv.env['API_ROOT']!));
dio.interceptors.add(PrettyDioLogger());
dioAdapter = DioAdapter(dio: dio);
Expand Down
80 changes: 77 additions & 3 deletions waydowntown_app/test/request_run_route_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,85 @@ import 'dart:io';

import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:http_mock_adapter/http_mock_adapter.dart';
import 'package:pretty_dio_logger/pretty_dio_logger.dart';
import 'package:waydowntown/routes/request_run_route.dart';
import 'package:waydowntown/routes/run_launch_route.dart';
import 'package:waydowntown/services/user_service.dart';

import './test_helpers.dart';
import './test_helpers.mocks.dart';

class TestAssetBundle extends CachingAssetBundle {
final Map<String, dynamic> _assets = {};

void addAsset(String key, String value) {
_assets[key] = value;
}

@override
void clear() {
_assets.clear();
}

@override
Future<String> loadString(String key, {bool cache = true}) async {
if (_assets.containsKey(key)) {
if (_assets[key] is String) {
return _assets[key]!;
}
throw FlutterError('Asset is not a string: $key');
}
throw FlutterError('Asset not found: $key');
}

@override
Future<ByteData> load(String key) async {
if (_assets.containsKey(key)) {
if (_assets[key] is String) {
return ByteData.view(
Uint8List.fromList(_assets[key]!.codeUnits).buffer);
} else if (_assets[key] is List<int>) {
return ByteData.view(Uint8List.fromList(_assets[key]!).buffer);
}
}
throw FlutterError('Asset not found: $key');
}
}

void main() {
late Dio dio;
late DioAdapter dioAdapter;
late TestAssetBundle testAssetBundle;
late MockPhoenixSocket mockSocket;

setUp(() {
setUp(() async {
dotenv.testLoad(fileInput: File('.env').readAsStringSync());

dio = Dio(BaseOptions(baseUrl: dotenv.env['API_ROOT']!));
dio.interceptors.add(PrettyDioLogger());
dioAdapter = DioAdapter(dio: dio);

FlutterSecureStorage.setMockInitialValues({});
await UserService.setUserData('user1', 'user1@example.com', false);
await UserService.setTokens('test_token', 'test_renewal_token');

testAssetBundle = TestAssetBundle();
testAssetBundle.addAsset('assets/concepts.yaml', '''
bluetooth_collector:
name: Bluetooth Collector
instructions: Collect Bluetooth devices
''');

(mockSocket, _, _) = TestHelpers.setupMockSocket();
});

tearDown(() {
testAssetBundle.clear();
});

const requestRunRoute = '/waydowntown/runs';
Expand All @@ -32,7 +92,14 @@ void main() {
TestHelpers.setupMockRunResponse(dioAdapter,
route: requestRunRoute, run: mockGame);

await tester.pumpWidget(MaterialApp(home: RequestRunRoute(dio: dio)));
await tester.pumpWidget(
MaterialApp(
home: DefaultAssetBundle(
bundle: testAssetBundle,
child: RequestRunRoute(dio: dio, testSocket: mockSocket),
),
),
);
await tester.pumpAndSettle();

expect(find.byType(RunLaunchRoute), findsOneWidget);
Expand All @@ -42,7 +109,14 @@ void main() {
(WidgetTester tester) async {
TestHelpers.setupMockErrorResponse(dioAdapter, requestRunRoute, data: {});

await tester.pumpWidget(MaterialApp(home: RequestRunRoute(dio: dio)));
await tester.pumpWidget(
MaterialApp(
home: DefaultAssetBundle(
bundle: testAssetBundle,
child: RequestRunRoute(dio: dio, testSocket: mockSocket),
),
),
);
await tester.pumpAndSettle();

expect(find.text('Error fetching game'), findsOneWidget);
Expand Down
Loading
Loading