Merge branch 'claude/festive-einstein-384c45'
Ego-centric trust replaces the global Duniter WoT, plus Wallapop-style ratings v1 (kind 30778). Conflicts resolved: pubspec assets (kept seed_saving, dropped trust referents), chat_screen (avatars/no-links from main + rating strip and simplified TrustCubit from the branch), strings.g.dart regenerated from merged sources.
This commit is contained in:
commit
15511ee761
40 changed files with 1777 additions and 876 deletions
|
|
@ -1,45 +0,0 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:tane/services/trust_referents.dart';
|
||||
|
||||
import '../support/test_support.dart';
|
||||
|
||||
void main() {
|
||||
const npub =
|
||||
'npub180cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsyjh6w6';
|
||||
const hex =
|
||||
'3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d';
|
||||
|
||||
// No bundled asset in the unit test bundle → _bundled() degrades to empty, so
|
||||
// these exercise the user-added set (the cold-start bootstrap path).
|
||||
late TrustReferents referents;
|
||||
setUp(() => referents = TrustReferents(InMemorySecretStore()));
|
||||
|
||||
test('starts empty', () async {
|
||||
expect(await referents.all(), isEmpty);
|
||||
expect(await referents.userAdded(), isEmpty);
|
||||
});
|
||||
|
||||
test('adds a referent from an npub, storing hex', () async {
|
||||
final stored = await referents.add(npub);
|
||||
expect(stored, hex);
|
||||
expect(await referents.userAdded(), {hex});
|
||||
expect(await referents.all(), contains(hex));
|
||||
});
|
||||
|
||||
test('accepts a hex key directly and is idempotent', () async {
|
||||
await referents.add(hex);
|
||||
await referents.add(npub); // same identity, npub form
|
||||
expect(await referents.userAdded(), {hex});
|
||||
});
|
||||
|
||||
test('rejects an invalid identity code', () async {
|
||||
expect(() => referents.add('not-a-key'), throwsFormatException);
|
||||
expect(await referents.userAdded(), isEmpty);
|
||||
});
|
||||
|
||||
test('removes a referent', () async {
|
||||
await referents.add(hex);
|
||||
await referents.remove(hex);
|
||||
expect(await referents.userAdded(), isEmpty);
|
||||
});
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
import 'package:commons_core/commons_core.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:tane/services/wot_settings.dart';
|
||||
|
||||
import '../support/test_support.dart';
|
||||
|
||||
void main() {
|
||||
late WotSettings settings;
|
||||
setUp(() => settings = WotSettings(InMemorySecretStore()));
|
||||
|
||||
test('defaults to the Duniter parameters', () async {
|
||||
expect(await settings.params(), WotParams.duniter);
|
||||
});
|
||||
|
||||
test('persists saved parameters', () async {
|
||||
const custom = WotParams(
|
||||
sigQty: 3, stepMax: 4, sigValidity: Duration(days: 180));
|
||||
await settings.save(custom);
|
||||
expect(await settings.params(), custom);
|
||||
});
|
||||
|
||||
test('reset restores the Duniter defaults', () async {
|
||||
await settings.save(const WotParams(
|
||||
sigQty: 1, stepMax: 1, sigValidity: Duration(days: 30)));
|
||||
await settings.reset();
|
||||
expect(await settings.params(), WotParams.duniter);
|
||||
});
|
||||
}
|
||||
162
apps/app_seeds/test/state/peer_rating_cubit_test.dart
Normal file
162
apps/app_seeds/test/state/peer_rating_cubit_test.dart
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
import 'package:commons_core/commons_core.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:tane/state/peer_rating_cubit.dart';
|
||||
|
||||
/// In-memory [RatingTransport]: rate/retract act as this device's identity,
|
||||
/// one live rating per (rater, subject) like the Nostr transport.
|
||||
class FakeRatingTransport implements RatingTransport {
|
||||
FakeRatingTransport(this.selfId);
|
||||
final String selfId;
|
||||
final Map<String, Map<String, Rating>> _bySubject = {}; // subject -> rater
|
||||
|
||||
/// Test helper: record an arbitrary rater→subject rating.
|
||||
void addRating(String rater, String subject, int stars,
|
||||
{String comment = ''}) =>
|
||||
(_bySubject[subject] ??= {})[rater] = Rating(
|
||||
rater: rater,
|
||||
subject: subject,
|
||||
stars: stars,
|
||||
ratedAt: DateTime(2026),
|
||||
comment: comment,
|
||||
);
|
||||
|
||||
@override
|
||||
Future<void> rate({
|
||||
required String subjectPubkey,
|
||||
required int stars,
|
||||
String comment = '',
|
||||
}) async =>
|
||||
addRating(selfId, subjectPubkey, stars, comment: comment);
|
||||
|
||||
@override
|
||||
Future<void> retract({required String subjectPubkey}) async =>
|
||||
_bySubject[subjectPubkey]?.remove(selfId);
|
||||
|
||||
@override
|
||||
Future<List<Rating>> ratingsOf(String subjectPubkey) async =>
|
||||
List.of(_bySubject[subjectPubkey]?.values ?? const <Rating>[]);
|
||||
|
||||
@override
|
||||
Future<Rating?> myRatingOf(String subjectPubkey) async =>
|
||||
_bySubject[subjectPubkey]?[selfId];
|
||||
|
||||
@override
|
||||
Future<void> close() async {}
|
||||
}
|
||||
|
||||
/// Minimal trust transport exposing a fixed certification graph, for the
|
||||
/// circle weighting.
|
||||
class FakeTrustTransport implements TrustTransport {
|
||||
final List<Certification> certs = [];
|
||||
|
||||
void addCert(String issuer, String subject) => certs.add(Certification(
|
||||
issuer: issuer,
|
||||
subject: subject,
|
||||
issuedAt: DateTime(2026),
|
||||
));
|
||||
|
||||
@override
|
||||
Future<List<Certification>> allCertifications() async => List.of(certs);
|
||||
|
||||
@override
|
||||
Future<Set<String>> certifiersOf(String subjectPubkey) async => {
|
||||
for (final c in certs)
|
||||
if (c.subject == subjectPubkey) c.issuer,
|
||||
};
|
||||
|
||||
@override
|
||||
Future<void> certify({
|
||||
required String subjectPubkey,
|
||||
Duration validity = const Duration(days: 365),
|
||||
String note = '',
|
||||
}) async {}
|
||||
|
||||
@override
|
||||
Future<void> revoke({required String subjectPubkey}) async {}
|
||||
|
||||
@override
|
||||
Future<void> close() async {}
|
||||
}
|
||||
|
||||
void main() {
|
||||
const me = 'me';
|
||||
const peer = 'peer';
|
||||
|
||||
test('aggregates count and average over active ratings', () async {
|
||||
final ratings = FakeRatingTransport(me)
|
||||
..addRating('a', peer, 5)
|
||||
..addRating('b', peer, 2);
|
||||
final cubit = PeerRatingCubit(ratings, peerPubkey: peer, selfPubkey: me);
|
||||
await cubit.load();
|
||||
expect(cubit.state.count, 2);
|
||||
expect(cubit.state.average, closeTo(3.5, 0.001));
|
||||
expect(cubit.state.circleCount, 0); // no trust graph wired
|
||||
expect(cubit.state.iRated, isFalse);
|
||||
await cubit.close();
|
||||
});
|
||||
|
||||
test('counts ratings coming from your circle', () async {
|
||||
final ratings = FakeRatingTransport(me)
|
||||
..addRating('friend', peer, 5) // you vouch for 'friend'
|
||||
..addRating('stranger', peer, 1);
|
||||
final trust = FakeTrustTransport()..addCert(me, 'friend');
|
||||
final cubit = PeerRatingCubit(
|
||||
ratings,
|
||||
peerPubkey: peer,
|
||||
selfPubkey: me,
|
||||
trust: trust,
|
||||
);
|
||||
await cubit.load();
|
||||
expect(cubit.state.count, 2);
|
||||
expect(cubit.state.circleCount, 1);
|
||||
await cubit.close();
|
||||
});
|
||||
|
||||
test('rate publishes then reloads; re-rating replaces', () async {
|
||||
final ratings = FakeRatingTransport(me);
|
||||
final cubit = PeerRatingCubit(ratings, peerPubkey: peer, selfPubkey: me);
|
||||
await cubit.load();
|
||||
|
||||
await cubit.rate(4, comment: 'Nice seeds');
|
||||
expect(cubit.state.myStars, 4);
|
||||
expect(cubit.state.myComment, 'Nice seeds');
|
||||
expect(cubit.state.count, 1);
|
||||
|
||||
await cubit.rate(2);
|
||||
expect(cubit.state.myStars, 2);
|
||||
expect(cubit.state.count, 1); // replaced, not piled up
|
||||
await cubit.close();
|
||||
});
|
||||
|
||||
test('retract removes your rating', () async {
|
||||
final ratings = FakeRatingTransport(me)..addRating(me, peer, 3);
|
||||
final cubit = PeerRatingCubit(ratings, peerPubkey: peer, selfPubkey: me);
|
||||
await cubit.load();
|
||||
expect(cubit.state.iRated, isTrue);
|
||||
|
||||
await cubit.retract();
|
||||
expect(cubit.state.iRated, isFalse);
|
||||
expect(cubit.state.count, 0);
|
||||
await cubit.close();
|
||||
});
|
||||
|
||||
test('never rates self', () async {
|
||||
final ratings = FakeRatingTransport(me);
|
||||
final cubit = PeerRatingCubit(ratings, peerPubkey: me, selfPubkey: me);
|
||||
await cubit.load();
|
||||
await cubit.rate(5);
|
||||
expect(cubit.state.iRated, isFalse);
|
||||
expect(cubit.state.count, 0);
|
||||
await cubit.close();
|
||||
});
|
||||
|
||||
test('offline (no transport) never throws', () async {
|
||||
final cubit = PeerRatingCubit(null, peerPubkey: peer, selfPubkey: me);
|
||||
expect(cubit.isOnline, isFalse);
|
||||
await cubit.load();
|
||||
await cubit.rate(5);
|
||||
await cubit.retract();
|
||||
expect(cubit.state.loading, isFalse);
|
||||
await cubit.close();
|
||||
});
|
||||
}
|
||||
|
|
@ -83,6 +83,7 @@ void main() {
|
|||
final inCircle = TrustCubit(transport, peerPubkey: peer, selfPubkey: me);
|
||||
await inCircle.load();
|
||||
expect(inCircle.state.knownToYou, isTrue);
|
||||
expect(inCircle.state.tier, TrustTier.inYourCircle);
|
||||
await inCircle.close();
|
||||
|
||||
final outside = TrustCubit(transport, peerPubkey: 'other', selfPubkey: me);
|
||||
|
|
@ -91,39 +92,6 @@ void main() {
|
|||
await outside.close();
|
||||
});
|
||||
|
||||
test('network membership: enough referent vouches → member', () async {
|
||||
const params =
|
||||
WotParams(sigQty: 2, stepMax: 5, sigValidity: Duration(days: 365));
|
||||
final transport = FakeTrustTransport(me)
|
||||
..addCert('r1', peer)
|
||||
..addCert('r2', peer);
|
||||
final cubit = TrustCubit(
|
||||
transport,
|
||||
peerPubkey: peer,
|
||||
selfPubkey: me,
|
||||
referents: {'r1', 'r2'},
|
||||
params: params,
|
||||
);
|
||||
await cubit.load();
|
||||
expect(cubit.state.networkBootstrapped, isTrue);
|
||||
expect(cubit.state.isNetworkMember, isTrue);
|
||||
expect(cubit.state.tier, TrustTier.networkMember);
|
||||
await cubit.close();
|
||||
});
|
||||
|
||||
test('with no referents, membership is undetermined (not a member)',
|
||||
() async {
|
||||
final transport = FakeTrustTransport(me)
|
||||
..addCert('r1', peer)
|
||||
..addCert('r2', peer);
|
||||
// referents default to {} → the trust net isn't seeded.
|
||||
final cubit = TrustCubit(transport, peerPubkey: peer, selfPubkey: me);
|
||||
await cubit.load();
|
||||
expect(cubit.state.networkBootstrapped, isFalse);
|
||||
expect(cubit.state.isNetworkMember, isFalse);
|
||||
await cubit.close();
|
||||
});
|
||||
|
||||
test('tier falls back to vouched, then unknown', () async {
|
||||
final vouched = TrustCubit(
|
||||
FakeTrustTransport(me)..addCert('someone', peer),
|
||||
|
|
|
|||
124
apps/app_seeds/test/ui/rating_sheet_test.dart
Normal file
124
apps/app_seeds/test/ui/rating_sheet_test.dart
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
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);
|
||||
});
|
||||
}
|
||||
176
apps/app_seeds/test/ui/your_people_screen_test.dart
Normal file
176
apps/app_seeds/test/ui/your_people_screen_test.dart
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
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/services/profile_cache.dart';
|
||||
import 'package:tane/services/social_connection.dart';
|
||||
import 'package:tane/services/social_service.dart';
|
||||
import 'package:tane/services/social_settings.dart';
|
||||
import 'package:tane/ui/your_people_screen.dart';
|
||||
|
||||
import '../support/test_support.dart';
|
||||
|
||||
/// In-memory [TrustTransport] with a mutable certification graph.
|
||||
class FakeTrustTransport implements TrustTransport {
|
||||
FakeTrustTransport(this.selfId);
|
||||
final String selfId;
|
||||
final List<Certification> certs = [];
|
||||
|
||||
void addCert(String issuer, String subject) => certs.add(Certification(
|
||||
issuer: issuer,
|
||||
subject: subject,
|
||||
issuedAt: DateTime(2026),
|
||||
));
|
||||
|
||||
@override
|
||||
Future<List<Certification>> allCertifications() async => List.of(certs);
|
||||
|
||||
@override
|
||||
Future<Set<String>> certifiersOf(String subjectPubkey) async => {
|
||||
for (final c in certs)
|
||||
if (c.subject == subjectPubkey) c.issuer,
|
||||
};
|
||||
|
||||
@override
|
||||
Future<void> certify({
|
||||
required String subjectPubkey,
|
||||
Duration validity = const Duration(days: 365),
|
||||
String note = '',
|
||||
}) async =>
|
||||
addCert(selfId, subjectPubkey);
|
||||
|
||||
@override
|
||||
Future<void> revoke({required String subjectPubkey}) async => certs
|
||||
.removeWhere((c) => c.issuer == selfId && c.subject == subjectPubkey);
|
||||
|
||||
@override
|
||||
Future<void> close() async {}
|
||||
}
|
||||
|
||||
/// A [SocialSession] stand-in that only carries the trust transport — the
|
||||
/// screen under test never touches the other transports.
|
||||
class FakeSession implements SocialSession {
|
||||
FakeSession(this.trust);
|
||||
|
||||
@override
|
||||
final TrustTransport trust;
|
||||
|
||||
@override
|
||||
OfferTransport get offers => throw UnimplementedError();
|
||||
@override
|
||||
MessageTransport get messages => throw UnimplementedError();
|
||||
@override
|
||||
RatingTransport get ratings => throw UnimplementedError();
|
||||
@override
|
||||
ProfileTransport get profile => throw UnimplementedError();
|
||||
@override
|
||||
SyncTransport get sync => throw UnimplementedError();
|
||||
@override
|
||||
Future<void> close() async {}
|
||||
}
|
||||
|
||||
void main() {
|
||||
const seedHex =
|
||||
'000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f';
|
||||
final peerA = 'aa' * 32;
|
||||
final peerB = 'bb' * 32;
|
||||
|
||||
late SocialService social;
|
||||
|
||||
setUp(() async {
|
||||
social = await SocialService.fromRootSeedHex(seedHex);
|
||||
LocaleSettings.setLocaleSync(AppLocale.en);
|
||||
});
|
||||
|
||||
Widget app(SocialConnection connection, {ProfileCache? cache}) =>
|
||||
TranslationProvider(
|
||||
child: MaterialApp(
|
||||
locale: AppLocale.en.flutterLocale,
|
||||
supportedLocales: AppLocaleUtils.supportedLocales,
|
||||
localizationsDelegates: const [
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
],
|
||||
home: YourPeopleScreen(
|
||||
social: social,
|
||||
connection: connection,
|
||||
profileCache: cache,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
SocialConnection online(TrustTransport trust) => SocialConnection(
|
||||
social: social,
|
||||
settings: SocialSettings(InMemorySecretStore()),
|
||||
open: (_) async => FakeSession(trust) as SocialSession,
|
||||
);
|
||||
|
||||
testWidgets('offline shows the friendly note', (tester) async {
|
||||
final settings = SocialSettings(InMemorySecretStore());
|
||||
await settings.setRelayUrls(const []); // offline: don't hit the network
|
||||
final connection = SocialConnection(social: social, settings: settings);
|
||||
|
||||
await tester.pumpWidget(app(connection));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(
|
||||
find.text("You're offline — try again when you're connected"),
|
||||
findsOneWidget,
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('lists who you vouch for and who vouches for you, with names',
|
||||
(tester) async {
|
||||
final trust = FakeTrustTransport(social.publicKeyHex)
|
||||
..addCert(social.publicKeyHex, peerA) // you vouch for A
|
||||
..addCert(peerB, social.publicKeyHex) // B vouches for you
|
||||
..addCert(peerA, peerB); // unrelated edge, must not show
|
||||
final cache = ProfileCache(InMemorySecretStore());
|
||||
await cache.setName(peerA, 'Alice');
|
||||
|
||||
await tester.pumpWidget(app(online(trust), cache: cache));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('You vouch for'), findsOneWidget);
|
||||
expect(find.text('They vouch for you'), findsOneWidget);
|
||||
expect(find.text('Alice'), findsOneWidget); // cached name wins
|
||||
expect(find.text(shortPubkey(peerB)), findsOneWidget); // fallback
|
||||
expect(find.byKey(Key('yourPeople.given.$peerA')), findsOneWidget);
|
||||
expect(find.byKey(Key('yourPeople.received.$peerB')), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('empty states show gentle notes', (tester) async {
|
||||
await tester
|
||||
.pumpWidget(app(online(FakeTrustTransport(social.publicKeyHex))));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(
|
||||
find.textContaining("You don't vouch for anyone yet"),
|
||||
findsOneWidget,
|
||||
);
|
||||
expect(find.text('No one vouches for you yet'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('revoking removes the vouch after confirming', (tester) async {
|
||||
final trust = FakeTrustTransport(social.publicKeyHex)
|
||||
..addCert(social.publicKeyHex, peerA);
|
||||
|
||||
await tester.pumpWidget(app(online(trust)));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.text('Stop vouching'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('Stop vouching for this person?'), findsOneWidget);
|
||||
|
||||
await tester.tap(find.text('Stop vouching').last); // dialog action
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(trust.certs, isEmpty);
|
||||
expect(
|
||||
find.textContaining("You don't vouch for anyone yet"),
|
||||
findsOneWidget,
|
||||
);
|
||||
});
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue