Files
Europa/Nuvolari/app/test/app_test.dart
T
Alby96andClaude Opus 5 85b2c99949 Scaffold the Flutter app with Italian l10n and region configuration
Creates the app skeleton and the seam everything else in the project hangs
off: region-specific data lives in an asset file, not in code, so adding a
region later is a new JSON file rather than a refactor.

Notable choices:

- applicationId and namespace are it.nuvolari.app rather than the doubled
  it.nuvolari.nuvolari that `flutter create` produces, with the Kotlin package
  and the iOS bundle identifiers moved to match.
- SDK levels are pinned instead of inherited from `flutter.*`. Google Play
  requires API 36, and that is a release blocker rather than something to let a
  Flutter upgrade change silently. minSdk 24 is the highest floor the planned
  dependencies impose.
- Riverpod and Dio-free for now, no code generation: see docs/stack-decisions.md.
- Italian is the l10n source language, so app_it.arb is the template rather
  than a translation of an English original.

The region parser rejects rather than repairs. An inverted bounding box, a map
centre outside its own bounds, an unknown adapter name, a duplicate zone code
or an empty attribution list all throw with the offending field named. Each of
those would otherwise fail silently and visibly wrong: a swapped latitude and
longitude renders the radar in the wrong place, an unknown adapter falling back
to mock would show demo frames where live data was expected, and a missing
attribution is a licence violation rather than a cosmetic gap.

Tests run against the asset that actually ships and against a captured copy of
the live ARPA CAP feed, so the eleven zone codes in the config are checked
against the eleven the feed really emits rather than against a list retyped
from documentation.

Verified: dart format clean, flutter analyze 0 issues, 50 tests passing,
flutter build appbundle --debug produces an AAB with applicationId
it.nuvolari.app, minSdk 24, targetSdk 36.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-10 11:24:26 +02:00

55 lines
1.9 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:nuvolari/l10n/app_localizations.dart';
import 'package:nuvolari/main.dart';
void main() {
testWidgets('starts up and shows the Italian UI', (tester) async {
await tester.pumpWidget(const ProviderScope(child: NuvolariApp()));
await tester.pump();
expect(find.text('Nuvolari'), findsWidgets);
});
group('AppLocalizations', () {
test('supports Italian', () {
expect(
AppLocalizations.supportedLocales.map((locale) => locale.languageCode),
contains('it'),
);
});
test('resolves Italian strings', () async {
final l10n = await AppLocalizations.delegate.load(const Locale('it'));
expect(l10n.appTitle, 'Nuvolari');
expect(l10n.navRadar, 'Radar');
expect(l10n.navForecast, 'Previsioni');
expect(l10n.navAlerts, 'Allerte');
});
// The disclaimer is a compliance requirement, not copy: it must state both
// that the app is independent and that official channels take precedence.
test(
'the disclaimer states independence and defers to official channels',
() async {
final l10n = await AppLocalizations.delegate.load(const Locale('it'));
expect(l10n.sourcesDisclaimerBody, contains('indipendente'));
expect(l10n.sourcesDisclaimerBody, contains('Arpa Piemonte'));
expect(l10n.sourcesDisclaimerBody, contains('Protezione Civile'));
expect(l10n.sourcesDisclaimerBody, contains('ufficiali'));
},
);
test('pluralises the data age message', () async {
final l10n = await AppLocalizations.delegate.load(const Locale('it'));
expect(l10n.dataAgeMinutes(0), 'Aggiornato ora');
expect(l10n.dataAgeMinutes(1), 'Aggiornato 1 minuto fa');
expect(l10n.dataAgeMinutes(12), 'Aggiornato 12 minuti fa');
});
});
}