import 'dart:io'; import 'package:flutter_test/flutter_test.dart'; import 'package:halkyon_app/halkyon_app.dart'; import 'package:halkyon_app/testing.dart'; import 'package:material_ui/material_ui.dart'; import 'package:path/path.dart' as p; void main() { final files = { '0.18.0.en.txt': '- Eighteen\n', '0.19.0.en.txt': '- Nineteen, first\n- Nineteen, second\n', '0.19.0.de.txt': '- Neunzehn\n', '0.20.0.en.txt': '- Twenty\n\n', '0.21.0.en.txt': '- Not out yet\n', 'notes.txt': '- Not a release\n', }; /// [releases] as lists, which compare by their contents. List flat(Iterable releases) => [ for (final release in releases) [release?.version, release?.changes], ]; WhatsNew whatsNew(MemoryWhatsNewStore store, {String version = '0.20.0'}) => WhatsNew(MemoryWhatsNewSource(files), store, version: version); group('unseen', () { test('after an update, every release since the one shown last, newest ' 'first, and they count as seen', () async { final store = MemoryWhatsNewStore(seen: '0.18.0'); final releases = await whatsNew( store, ).unseen(language: 'en', usedBefore: true); expect(flat(releases), [ [ '0.20.0', ['Twenty'], ], [ '0.19.0', ['Nineteen, first', 'Nineteen, second'], ], ]); expect(store.seen, '0.20.0'); }); test( 'in the language asked for where there is one, else English', () async { final releases = await whatsNew( MemoryWhatsNewStore(seen: '0.18.0'), ).unseen(language: 'de', usedBefore: true); expect(flat(releases), [ [ '0.20.0', ['Twenty'], ], [ '0.19.0', ['Neunzehn'], ], ]); }, ); test('nothing once seen', () async { final store = MemoryWhatsNewStore(seen: '0.18.0'); await whatsNew(store).unseen(language: 'en', usedBefore: true); expect( await whatsNew(store).unseen(language: 'en', usedBefore: true), isEmpty, ); }); test('nothing on a fresh install, which counts as seen', () async { final store = MemoryWhatsNewStore(); expect( await whatsNew(store).unseen(language: 'en', usedBefore: false), isEmpty, ); expect(store.seen, '0.20.0'); }); test('a device used before the app showed what is new gets the version ' 'that runs alone', () async { final releases = await whatsNew( MemoryWhatsNewStore(), ).unseen(language: 'en', usedBefore: true); expect(flat(releases), [ [ '0.20.0', ['Twenty'], ], ]); }); test('nothing after going back to an older version, which counts as seen ' 'from then', () async { final store = MemoryWhatsNewStore(seen: '0.21.0'); expect( await whatsNew(store).unseen(language: 'en', usedBefore: true), isEmpty, ); expect(store.seen, '0.20.0'); }); test('nothing for a version without a release number, such as a ' "development build's", () async { final store = MemoryWhatsNewStore(seen: '0.18.0'); expect( await whatsNew( store, version: 'dev', ).unseen(language: 'en', usedBefore: true), isEmpty, ); expect(store.seen, '0.18.0'); }); }); test("current is the running version's changes, null without", () async { expect( flat([await whatsNew(MemoryWhatsNewStore()).current(language: 'de')]), [ [ '0.20.0', ['Twenty'], ], ], ); expect( await whatsNew( MemoryWhatsNewStore(), version: '0.17.0', ).current(language: 'en'), isNull, ); }); test("changesOf takes the lines starting with '- '", () { expect(changesOf('Intro\n- One\n - Two \n\n-Three\n'), ['One', 'Two']); }); group('FileWhatsNewStore', () { late Directory folder; setUp(() => folder = Directory.systemTemp.createTempSync('whats_new')); tearDown(() => folder.deleteSync(recursive: true)); test('keeps the version shown last; none before', () async { final file = File(p.join(folder.path, 'whats_new.json')); expect(await FileWhatsNewStore(file).read(), isNull); await FileWhatsNewStore(file).write('0.20.0'); expect(await FileWhatsNewStore(file).read(), '0.20.0'); }); test('reads nothing from a broken file', () async { final file = File(p.join(folder.path, 'whats_new.json')) ..writeAsStringSync('{'); expect(await FileWhatsNewStore(file).read(), isNull); }); }); group('widgets', () { Future show( WidgetTester tester, { required String? seen, Widget child = const WhatsNewOnUpdate(child: Text('Tabs')), }) async { final store = MemoryWhatsNewStore(seen: seen); await tester.pumpWidget( WhatsNewScope( whatsNew: whatsNew(store), usedBefore: true, child: testApp(Scaffold(body: child)), ), ); await tester.pumpAndSettle(); return store; } testWidgets('after an update, the sheet lists each release since', ( tester, ) async { await show(tester, seen: '0.18.0'); expect(find.text("What's new"), findsOneWidget); expect(find.text('Version 0.20.0'), findsOneWidget); expect(find.text('Twenty'), findsOneWidget); expect(find.text('Version 0.19.0'), findsOneWidget); expect(find.text('Nineteen, second'), findsOneWidget); expect( tester.getTopLeft(find.text('Version 0.20.0')).dy, lessThan(tester.getTopLeft(find.text('Version 0.19.0')).dy), ); }); testWidgets('once seen, no sheet', (tester) async { await show(tester, seen: '0.20.0'); expect(find.text("What's new"), findsNothing); expect(find.text('Tabs'), findsOneWidget); }); testWidgets("Settings' tile shows the running version's changes", ( tester, ) async { await show(tester, seen: '0.20.0', child: const WhatsNewTile()); expect(find.text('Version 0.20.0'), findsOneWidget); await tester.tap(find.text("What's new")); await tester.pumpAndSettle(); expect(find.text('Twenty'), findsOneWidget); }); testWidgets('without a scope, neither the sheet nor the tile', ( tester, ) async { await tester.pumpWidget( testApp( const Scaffold( body: WhatsNewOnUpdate(child: WhatsNewTile()), ), ), ); await tester.pumpAndSettle(); expect(find.text("What's new"), findsNothing); }); }); }