Merge branch 'spike/block2-derisking'

This commit is contained in:
vjrj 2026-07-10 12:20:52 +02:00
commit 867bf38c5a
18 changed files with 308 additions and 150 deletions

View file

@ -1,6 +1,3 @@
import 'dart:async';
import 'package:commons_core/commons_core.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
@ -14,6 +11,12 @@ import 'package:tane/ui/market_screen.dart';
import '../support/test_support.dart';
// NOTE: discovery/offer-list rendering is covered by offers_cubit_test.dart
// (plain `test` + pumpEventQueue). We deliberately do NOT drive discovery
// through `testWidgets` here: the transient "searching" CircularProgressIndicator
// never settles under pumpAndSettle and hangs the runner. These widget tests
// only cover the offline/setup paths, which have no live stream.
/// A location provider returning a fixed coarse position.
class FakeLocation implements CoarseLocationProvider {
FakeLocation(this.lat, this.lon);
@ -24,32 +27,7 @@ class FakeLocation implements CoarseLocationProvider {
(lat: lat, lon: lon);
}
/// In-memory offer transport: discover matches by geohash prefix.
class FakeOfferTransport implements OfferTransport {
final List<Offer> offers;
FakeOfferTransport(this.offers);
@override
Future<PublishResult> publish(Offer offer) async =>
PublishResult(accepted: true, transportRef: offer.id);
@override
Stream<Offer> discover(DiscoveryQuery query) {
final c = StreamController<Offer>();
for (final o in offers) {
if (o.approxGeohash.startsWith(query.geohashPrefix)) c.add(o);
}
unawaited(c.close()); // finite: deliver buffered matches, then done
return c.stream;
}
@override
Future<void> retract(String offerId) async {}
@override
Future<void> close() async {}
}
Widget _wrapBody(OffersCubit cubit, {String? selfPubkey}) {
Widget _wrapBody(OffersCubit cubit) {
LocaleSettings.setLocaleSync(AppLocale.en);
return TranslationProvider(
child: MaterialApp(
@ -63,65 +41,31 @@ Widget _wrapBody(OffersCubit cubit, {String? selfPubkey}) {
home: Scaffold(
body: BlocProvider.value(
value: cubit,
child: MarketBody(onConfigure: () {}, selfPubkey: selfPubkey),
child: MarketBody(onConfigure: () {}),
),
),
),
);
}
Offer _offer(String id, String summary, OfferType type) => Offer(
id: id,
authorPubkeyHex: 'ab' * 32,
summary: summary,
type: type,
approxGeohash: 'sp3e9',
);
Widget _wrapMarket(SocialService social, SocialSettings settings,
{CoarseLocationProvider? location}) {
LocaleSettings.setLocaleSync(AppLocale.en);
return TranslationProvider(
child: MaterialApp(
locale: AppLocale.en.flutterLocale,
supportedLocales: AppLocaleUtils.supportedLocales,
localizationsDelegates: const [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
home: MarketScreen(social: social, settings: settings, location: location),
),
);
}
void main() {
testWidgets('shows a "You" badge on my own listing', (tester) async {
const me = 'aa';
final cubit = OffersCubit(FakeOfferTransport([
Offer(
id: '1',
authorPubkeyHex: me,
summary: 'Mine',
type: OfferType.gift,
approxGeohash: 'sp3e9',
),
]));
await tester.pumpWidget(_wrapBody(cubit, selfPubkey: me));
await cubit.discover('sp3');
// Pump a couple of frames (NOT pumpAndSettle the brief "searching" spinner
// never settles). The buffered offers deliver on a microtask.
await tester.pump();
await tester.pump();
expect(find.text('You'), findsOneWidget);
// My own listing offers no "Message yourself" button.
expect(find.text('Message'), findsNothing);
await cubit.close();
});
testWidgets('renders discovered offers with their type', (tester) async {
final cubit = OffersCubit(FakeOfferTransport([
_offer('1', 'Tomate rosa de Barbastro', OfferType.gift),
_offer('2', 'Judía del ganxet', OfferType.exchange),
]));
addTearDown(cubit.close);
await tester.pumpWidget(_wrapBody(cubit));
await cubit.discover('sp3');
// Pump a couple of frames (NOT pumpAndSettle the brief "searching" spinner
// never settles). The buffered offers deliver on a microtask.
await tester.pump();
await tester.pump();
expect(find.text('Tomate rosa de Barbastro'), findsOneWidget);
expect(find.text('Judía del ganxet'), findsOneWidget);
expect(find.text('Near you'), findsWidgets);
});
testWidgets('offline (no transport) shows the "set up sharing" prompt',
(tester) async {
final cubit = OffersCubit(null);
@ -139,22 +83,8 @@ void main() {
final social = await SocialService.fromRootSeedHex('00' * 32);
final settings = SocialSettings(InMemorySecretStore());
await settings.setRelayUrls(const []); // offline: don't hit the network
LocaleSettings.setLocaleSync(AppLocale.en);
await tester.pumpWidget(
TranslationProvider(
child: MaterialApp(
locale: AppLocale.en.flutterLocale,
supportedLocales: AppLocaleUtils.supportedLocales,
localizationsDelegates: const [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
home: MarketScreen(social: social, settings: settings),
),
),
);
await tester.pumpWidget(_wrapMarket(social, settings));
await tester.pumpAndSettle();
expect(find.text('Seeds near you'), findsOneWidget); // app bar title
@ -166,25 +96,9 @@ void main() {
final social = await SocialService.fromRootSeedHex('00' * 32);
final settings = SocialSettings(InMemorySecretStore());
await settings.setRelayUrls(const []); // offline: don't hit the network
LocaleSettings.setLocaleSync(AppLocale.en);
await tester.pumpWidget(
TranslationProvider(
child: MaterialApp(
locale: AppLocale.en.flutterLocale,
supportedLocales: AppLocaleUtils.supportedLocales,
localizationsDelegates: const [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
home: MarketScreen(
social: social,
settings: settings,
location: FakeLocation(41.39, 2.16), // Barcelona-ish
),
),
),
_wrapMarket(social, settings, location: FakeLocation(41.39, 2.16)),
);
await tester.pumpAndSettle();