diff --git a/pubspec.lock b/pubspec.lock index 8aacf7c..a9f336b 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -17,6 +17,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.3.0" + antlr4: + dependency: transitive + description: + name: antlr4 + sha256: "752b4a6e4ad97953652a2b2bbf5377f46c94b579d3372b50080c7e5858234a05" + url: "https://pub.dev" + source: hosted + version: "4.13.2" args: dependency: transitive description: @@ -49,6 +57,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.2" + cel: + dependency: transitive + description: + name: cel + sha256: "51d77e16424d41b5fdb0a239be4c8a0550d4dd3f952801d35375ddd90cfb49da" + url: "https://pub.dev" + source: hosted + version: "0.5.4+1" characters: dependency: transitive description: @@ -121,6 +137,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.19.1" + convert: + dependency: transitive + description: + name: convert + sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 + url: "https://pub.dev" + source: hosted + version: "3.1.2" cross_file: dependency: transitive description: @@ -145,6 +169,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.8" + dart_jsonwebtoken: + dependency: transitive + description: + name: dart_jsonwebtoken + sha256: cb79ed79baa02b4f59a597bf365873cbd83f9bb15273d63f7803802d21717c7d + url: "https://pub.dev" + source: hosted + version: "3.4.0" equatable: dependency: "direct main" description: @@ -161,6 +193,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.3" + fake_cloud_firestore: + dependency: "direct dev" + description: + name: fake_cloud_firestore + sha256: "95e01c95f956b31c62cb9fc54b8d51f32f1ae2909e6c0a5ce316da997e83a5c4" + url: "https://pub.dev" + source: hosted + version: "4.1.0+1" + fake_firebase_security_rules: + dependency: transitive + description: + name: fake_firebase_security_rules + sha256: "6af54bedfd6985451a9735f2cfac91ffe0128bfc92bf15e8544cd56c6941d6cc" + url: "https://pub.dev" + source: hosted + version: "0.5.4" ffi: dependency: transitive description: @@ -273,6 +321,14 @@ packages: url: "https://pub.dev" source: hosted version: "6.3.0" + firebase_auth_mocks: + dependency: "direct dev" + description: + name: firebase_auth_mocks + sha256: ee1076150ea51d181ae24d5f8e2686a1926b7cc54e25c0deaa8ac58705025551 + url: "https://pub.dev" + source: hosted + version: "0.15.1" firebase_auth_platform_interface: dependency: transitive description: @@ -298,7 +354,7 @@ packages: source: hosted version: "4.6.0" firebase_core_platform_interface: - dependency: transitive + dependency: "direct dev" description: name: firebase_core_platform_interface sha256: "0ecda14c1bfc9ed8cac303dd0f8d04a320811b479362a9a4efb14fd331a473ce" @@ -608,6 +664,22 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.1" + logger: + dependency: transitive + description: + name: logger + sha256: "25aee487596a6257655a1e091ec2ae66bc30e7af663592cc3a27e6591e05035c" + url: "https://pub.dev" + source: hosted + version: "2.7.0" + logging: + dependency: transitive + description: + name: logging + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 + url: "https://pub.dev" + source: hosted + version: "1.3.0" matcher: dependency: transitive description: @@ -640,6 +712,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.0" + mock_exceptions: + dependency: transitive + description: + name: mock_exceptions + sha256: "6e3e623712d2c6106ffe9e14732912522b565ddaa82a8dcee6cd4441b5984056" + url: "https://pub.dev" + source: hosted + version: "0.8.2" + more: + dependency: transitive + description: + name: more + sha256: e252628d2183cc09539b686abfbd9d8302675959b89a2a8146f5f4baca6ac5ba + url: "https://pub.dev" + source: hosted + version: "4.7.0" nested: dependency: transitive description: @@ -736,6 +824,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.8" + pointycastle: + dependency: transitive + description: + name: pointycastle + sha256: "92aa3841d083cc4b0f4709b5c74fd6409a3e6ba833ffc7dc6a8fee096366acf5" + url: "https://pub.dev" + source: hosted + version: "4.0.0" provider: dependency: transitive description: @@ -744,14 +840,22 @@ packages: url: "https://pub.dev" source: hosted version: "6.1.5+1" + rx: + dependency: transitive + description: + name: rx + sha256: "7f54bd39cc63a01c770c1de4b6ce8e135eb13119614cba2216bd9a93ccd29e56" + url: "https://pub.dev" + source: hosted + version: "0.4.0" rxdart: dependency: "direct main" description: name: rxdart - sha256: "0c7c0cedd93788d996e33041ffecda924cc54389199cde4e6a34b440f50044cb" + sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962" url: "https://pub.dev" source: hosted - version: "0.27.7" + version: "0.28.0" shared_preferences: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index d6c8ab2..3ff181a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -39,7 +39,7 @@ dependencies: equatable: ^2.0.5 meta: ^1.8.0 font_awesome_flutter: ^10.2.1 - rxdart: ^0.27.5 + rxdart: ^0.28.0 animated_text_kit: ^4.2.2 shared_preferences: ^2.2.2 google_fonts: ^6.2.1 @@ -48,6 +48,9 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter + firebase_core_platform_interface: ^6.0.0 + fake_cloud_firestore: ^4.0.0 + firebase_auth_mocks: ^0.15.0 # For information on the generic Dart part of this file, see the diff --git a/test/widget_test.dart b/test/widget_test.dart index b452b2a..ac74e14 100644 --- a/test/widget_test.dart +++ b/test/widget_test.dart @@ -1,7 +1,48 @@ +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:fake_cloud_firestore/fake_cloud_firestore.dart'; +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:firebase_auth_mocks/firebase_auth_mocks.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'package:firebase_core_platform_interface/firebase_core_platform_interface.dart'; +import 'package:firebase_core_platform_interface/test.dart'; +import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:postbox_game/main.dart'; import 'package:postbox_game/theme.dart'; +import 'package:postbox_game/user_repository.dart'; + +// --------------------------------------------------------------------------- +// Firebase mock setup +// --------------------------------------------------------------------------- + +/// Call once per test run (or in setUp) to initialise the Firebase platform +/// mock so that Firebase.initializeApp() succeeds without hitting real servers. +Future setupFirebaseMocks() async { + TestWidgetsFlutterBinding.ensureInitialized(); + setupFirebaseCoreMocks(); + await Firebase.initializeApp(); +} + +// --------------------------------------------------------------------------- +// Widget-level smoke tests +// --------------------------------------------------------------------------- void main() { + setUpAll(setupFirebaseMocks); + + group('App smoke tests', () { + testWidgets('PostboxGame widget tree renders without crashing', (tester) async { + await tester.pumpWidget(PostboxGame()); + await tester.pump(); // process one frame + // Should show SplashScreen or LoginScreen (Firebase is mocked) + expect(find.byType(MaterialApp), findsOneWidget); + }); + }); + + // --------------------------------------------------------------------------- + // AppSpacing unit tests (from phase4-polish) + // --------------------------------------------------------------------------- + group('AppSpacing', () { test('spacing scale is strictly increasing', () { expect(AppSpacing.xs, lessThan(AppSpacing.sm)); @@ -15,4 +56,70 @@ void main() { expect(AppSpacing.xs, greaterThan(0)); }); }); + + // --------------------------------------------------------------------------- + // UserRepository unit tests + // --------------------------------------------------------------------------- + + group('UserRepository', () { + late FakeFirebaseFirestore fakeFirestore; + late MockFirebaseAuth mockAuth; + late UserRepository repo; + + setUp(() { + fakeFirestore = FakeFirebaseFirestore(); + mockAuth = MockFirebaseAuth(); + repo = UserRepository( + firebaseAuth: mockAuth, + firestore: fakeFirestore, + ); + }); + + test('signUp writes displayName to Firestore', () async { + // MockFirebaseAuth creates a user automatically on signUp. + await repo.signUp(email: 'test@example.com', password: 'password123'); + + final uid = mockAuth.currentUser?.uid; + expect(uid, isNotNull); + + final doc = await fakeFirestore.collection('users').doc(uid).get(); + expect(doc.data()?['displayName'], equals('test')); + }); + + test('getDisplayName returns stored name', () async { + const uid = 'user-abc'; + await fakeFirestore.collection('users').doc(uid).set({'displayName': 'Postbox Pete'}); + + final name = await repo.getDisplayName(uid); + expect(name, equals('Postbox Pete')); + }); + + test('getDisplayName returns null for unknown uid', () async { + final name = await repo.getDisplayName('nonexistent-uid'); + expect(name, isNull); + }); + + test('isSignedIn returns false when no user', () async { + final signedIn = await repo.isSignedIn(); + expect(signedIn, isFalse); + }); + + test('isSignedIn returns true when a user is signed in', () async { + final auth = MockFirebaseAuth(signedIn: true); + final repo = UserRepository(firebaseAuth: auth, firestore: fakeFirestore); + expect(await repo.isSignedIn(), isTrue); + }); + + test('signUp writes email to Firestore', () async { + await repo.signUp(email: 'alice@example.com', password: 'password'); + final uid = mockAuth.currentUser?.uid; + expect(uid, isNotNull); + final doc = await fakeFirestore + .collection('users') + .doc(uid) + .get(); + expect(doc.data()?['email'], equals('alice@example.com')); + }); + }); + }