- SocialSession grows a ratings transport (fifth interface on the one shared channel). - PeerRatingCubit: count, locale-aware average, and circleCount — how many ratings come from your own circle (reuses the ego-centric trust rule), the signal that makes strangers' review-stuffing irrelevant. - Chat: slim rating strip under the trust banner; you can rate only someone you've talked with (soft anchor until the signed bilateral exchange form lands). Sheet with 5 stars + optional comment, pre-filled for editing, with a take-back action. - Offer detail: author's stars under their name, with 'N from people you know' when your circle rated them; nothing shown when unrated. - i18n ratings.* (en/es/pt/ast); tests for cubit and sheet.
124 lines
3.6 KiB
Dart
124 lines
3.6 KiB
Dart
import 'package:commons_core/commons_core.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_localizations/flutter_localizations.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:tane/i18n/strings.g.dart';
|
|
import 'package:tane/state/peer_rating_cubit.dart';
|
|
import 'package:tane/ui/rating_sheet.dart';
|
|
|
|
/// In-memory [RatingTransport] (one live rating per pair).
|
|
class FakeRatingTransport implements RatingTransport {
|
|
FakeRatingTransport(this.selfId);
|
|
final String selfId;
|
|
final Map<String, Rating> byRater = {};
|
|
|
|
@override
|
|
Future<void> rate({
|
|
required String subjectPubkey,
|
|
required int stars,
|
|
String comment = '',
|
|
}) async =>
|
|
byRater[selfId] = Rating(
|
|
rater: selfId,
|
|
subject: subjectPubkey,
|
|
stars: stars,
|
|
ratedAt: DateTime(2026),
|
|
comment: comment,
|
|
);
|
|
|
|
@override
|
|
Future<void> retract({required String subjectPubkey}) async =>
|
|
byRater.remove(selfId);
|
|
|
|
@override
|
|
Future<List<Rating>> ratingsOf(String subjectPubkey) async =>
|
|
List.of(byRater.values);
|
|
|
|
@override
|
|
Future<Rating?> myRatingOf(String subjectPubkey) async => byRater[selfId];
|
|
|
|
@override
|
|
Future<void> close() async {}
|
|
}
|
|
|
|
void main() {
|
|
const me = 'me';
|
|
const peer = 'peer';
|
|
|
|
late FakeRatingTransport transport;
|
|
late PeerRatingCubit cubit;
|
|
|
|
setUp(() async {
|
|
LocaleSettings.setLocaleSync(AppLocale.en);
|
|
transport = FakeRatingTransport(me);
|
|
cubit = PeerRatingCubit(transport, peerPubkey: peer, selfPubkey: me);
|
|
await cubit.load();
|
|
});
|
|
|
|
tearDown(() => cubit.close());
|
|
|
|
Widget host() => TranslationProvider(
|
|
child: MaterialApp(
|
|
locale: AppLocale.en.flutterLocale,
|
|
supportedLocales: AppLocaleUtils.supportedLocales,
|
|
localizationsDelegates: const [
|
|
GlobalMaterialLocalizations.delegate,
|
|
GlobalWidgetsLocalizations.delegate,
|
|
GlobalCupertinoLocalizations.delegate,
|
|
],
|
|
home: Scaffold(
|
|
body: Builder(
|
|
builder: (context) => Center(
|
|
child: TextButton(
|
|
onPressed: () => showRatingSheet(context, cubit),
|
|
child: const Text('open'),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
testWidgets('picking stars and saving publishes the rating',
|
|
(tester) async {
|
|
await tester.pumpWidget(host());
|
|
await tester.tap(find.text('open'));
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(find.text('Rate this person'), findsOneWidget);
|
|
// Save is disabled until stars are picked.
|
|
expect(
|
|
tester.widget<FilledButton>(find.byKey(const Key('rating.save'))).enabled,
|
|
isFalse,
|
|
);
|
|
|
|
await tester.tap(find.byKey(const Key('rating.star.4')));
|
|
await tester.pump();
|
|
await tester.enterText(
|
|
find.byKey(const Key('rating.comment')), 'Great swap');
|
|
await tester.tap(find.byKey(const Key('rating.save')));
|
|
await tester.pumpAndSettle();
|
|
|
|
final mine = transport.byRater[me]!;
|
|
expect(mine.stars, 4);
|
|
expect(mine.comment, 'Great swap');
|
|
expect(find.text('Rating saved'), findsOneWidget); // snackbar
|
|
});
|
|
|
|
testWidgets('opens pre-filled when you already rated; retract removes',
|
|
(tester) async {
|
|
await cubit.rate(3, comment: 'Fine');
|
|
|
|
await tester.pumpWidget(host());
|
|
await tester.tap(find.text('open'));
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(find.text('Edit your rating'), findsOneWidget);
|
|
expect(find.text('Fine'), findsOneWidget); // comment pre-filled
|
|
|
|
await tester.tap(find.byKey(const Key('rating.retract')));
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(transport.byRater, isEmpty);
|
|
});
|
|
}
|