首次远程推送
This commit is contained in:
@@ -12,6 +12,8 @@ import 'package:focus_seed/src/models/reflection_entry.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
setUp(() {
|
||||
SharedPreferences.setMockInitialValues({});
|
||||
});
|
||||
@@ -77,4 +79,99 @@ void main() {
|
||||
'reflection-yesterday',
|
||||
]);
|
||||
});
|
||||
|
||||
test('default focus duration persists and survives record cleanup', () async {
|
||||
final database = FocusSeedDatabase(NativeDatabase.memory());
|
||||
addTearDown(database.close);
|
||||
final store = FocusSeedStore(database: database);
|
||||
final controller = FocusSeedController(store: store);
|
||||
await controller.load();
|
||||
|
||||
final changed = await controller.setDefaultFocusMinutes(45);
|
||||
expect(changed, isTrue);
|
||||
expect(controller.state!.defaultFocusMinutes, 45);
|
||||
|
||||
final reloaded = FocusSeedController(store: store);
|
||||
await reloaded.load();
|
||||
expect(reloaded.state!.defaultFocusMinutes, 45);
|
||||
|
||||
final cleared = await reloaded.clearData();
|
||||
expect(cleared, isTrue);
|
||||
expect(reloaded.state!.defaultFocusMinutes, 45);
|
||||
});
|
||||
|
||||
test('completed focus sessions snapshot the active daily focus', () async {
|
||||
final database = FocusSeedDatabase(NativeDatabase.memory());
|
||||
addTearDown(database.close);
|
||||
final store = FocusSeedStore(database: database);
|
||||
final controller = FocusSeedController(store: store);
|
||||
await controller.load();
|
||||
|
||||
final savedFocus = await controller.setTodayAnchor('Review launch notes');
|
||||
final savedSession = await controller.completeFocusSession(
|
||||
10,
|
||||
actualSeconds: 60,
|
||||
);
|
||||
|
||||
expect(savedFocus, isTrue);
|
||||
expect(savedSession, isTrue);
|
||||
expect(controller.state!.sessions.single.intention, 'Review launch notes');
|
||||
|
||||
final reloaded = FocusSeedController(store: store);
|
||||
await reloaded.load();
|
||||
expect(reloaded.state!.sessions.single.intention, 'Review launch notes');
|
||||
});
|
||||
|
||||
test('progress notes can be backfilled for a past record date', () async {
|
||||
final database = FocusSeedDatabase(NativeDatabase.memory());
|
||||
addTearDown(database.close);
|
||||
final store = FocusSeedStore(database: database);
|
||||
final controller = FocusSeedController(store: store);
|
||||
await controller.load();
|
||||
|
||||
const pastDate = '2026-06-12';
|
||||
final saved = await controller.saveReflectionForDate(
|
||||
pastDate,
|
||||
'Recovered the next step.',
|
||||
Mood.clear,
|
||||
);
|
||||
|
||||
expect(saved, isTrue);
|
||||
expect(controller.state!.reflections.single.dateKey, pastDate);
|
||||
expect(
|
||||
controller.state!.reflections.single.note,
|
||||
'Recovered the next step.',
|
||||
);
|
||||
});
|
||||
|
||||
test('today anchor does not create a daily action object', () async {
|
||||
final database = FocusSeedDatabase(NativeDatabase.memory());
|
||||
addTearDown(database.close);
|
||||
final store = FocusSeedStore(database: database);
|
||||
final controller = FocusSeedController(store: store);
|
||||
await controller.load();
|
||||
|
||||
final savedToday = await controller.setTodayAnchor('Review launch notes');
|
||||
|
||||
expect(savedToday, isTrue);
|
||||
expect(controller.state!.activeAnchor(todayKey()), 'Review launch notes');
|
||||
expect(controller.state!.actions, isEmpty);
|
||||
});
|
||||
|
||||
test('focus can complete without a today anchor', () async {
|
||||
final database = FocusSeedDatabase(NativeDatabase.memory());
|
||||
addTearDown(database.close);
|
||||
final store = FocusSeedStore(database: database);
|
||||
final controller = FocusSeedController(store: store);
|
||||
await controller.load();
|
||||
|
||||
final savedSession = await controller.completeFocusSession(
|
||||
10,
|
||||
actualSeconds: 120,
|
||||
);
|
||||
|
||||
expect(savedSession, isTrue);
|
||||
expect(controller.state!.sessions.single.intention, isEmpty);
|
||||
expect(controller.state!.actions, isEmpty);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -52,7 +52,13 @@ void main() {
|
||||
),
|
||||
],
|
||||
sessions: [
|
||||
FocusSession(id: 'session-1', minutes: 25, completedAt: now),
|
||||
FocusSession(
|
||||
id: 'session-1',
|
||||
minutes: 25,
|
||||
actualSeconds: 90,
|
||||
completedAt: now,
|
||||
intention: 'Ship the local database',
|
||||
),
|
||||
],
|
||||
reflections: [
|
||||
ReflectionEntry(
|
||||
@@ -63,6 +69,7 @@ void main() {
|
||||
createdAt: now.add(const Duration(minutes: 1)),
|
||||
),
|
||||
],
|
||||
defaultFocusMinutes: 45,
|
||||
);
|
||||
|
||||
await store.save(state);
|
||||
@@ -72,7 +79,15 @@ void main() {
|
||||
expect(loaded.anchorDateKey, '2026-06-12');
|
||||
expect(loaded.habits.single.completedDates, {'2026-06-11', '2026-06-12'});
|
||||
expect(loaded.sessions.single.minutes, 25);
|
||||
expect(loaded.sessions.single.actualSeconds, 90);
|
||||
expect(loaded.sessions.single.actualMinutes, 2);
|
||||
expect(loaded.sessions.single.intention, 'Ship the local database');
|
||||
expect(loaded.reflections.single.note, 'Small scope kept it clean.');
|
||||
expect(loaded.defaultFocusMinutes, 45);
|
||||
final row = await database.select(database.focusSessionRows).getSingle();
|
||||
expect(row.plannedMinutes, 25);
|
||||
expect(row.actualSeconds, 90);
|
||||
expect(row.intentionText, 'Ship the local database');
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:focus_seed/src/models/app_state.dart';
|
||||
import 'package:focus_seed/src/models/focus_session.dart';
|
||||
import 'package:focus_seed/src/models/growth_stage.dart';
|
||||
import 'package:focus_seed/src/models/habit.dart';
|
||||
import 'package:focus_seed/src/models/mood.dart';
|
||||
import 'package:focus_seed/src/models/reflection_entry.dart';
|
||||
|
||||
void main() {
|
||||
const key = '2026-06-11';
|
||||
final date = DateTime(2026, 6, 11, 10);
|
||||
|
||||
test('growth stage follows real daily progress', () {
|
||||
const seed = AppState(
|
||||
anchor: '',
|
||||
anchorDateKey: key,
|
||||
habits: [],
|
||||
sessions: [],
|
||||
reflections: [],
|
||||
);
|
||||
expect(growthStageFor(seed, key), GrowthStage.seed);
|
||||
|
||||
final sprout = seed.copyWith(anchor: 'Ship one useful thing');
|
||||
expect(growthStageFor(sprout, key), GrowthStage.sprout);
|
||||
|
||||
final leaves = sprout.copyWith(
|
||||
sessions: [FocusSession(id: 'session-1', minutes: 25, completedAt: date)],
|
||||
habits: [
|
||||
Habit(
|
||||
id: 'habit-1',
|
||||
title: 'Plan one task',
|
||||
createdAt: date,
|
||||
completedDates: const {key},
|
||||
),
|
||||
],
|
||||
);
|
||||
expect(growthStageFor(leaves, key), GrowthStage.leaves);
|
||||
|
||||
final flower = leaves.copyWith(
|
||||
reflections: [
|
||||
ReflectionEntry(
|
||||
id: 'reflection-1',
|
||||
dateKey: key,
|
||||
note: 'A smaller scope made the work clearer.',
|
||||
mood: Mood.clear,
|
||||
createdAt: date,
|
||||
),
|
||||
],
|
||||
);
|
||||
expect(growthStageFor(flower, key), GrowthStage.flower);
|
||||
});
|
||||
|
||||
test('version one anchor data migrates to the current day', () {
|
||||
final state = AppState.fromJson(const {
|
||||
'version': 1,
|
||||
'anchor': 'Legacy intention',
|
||||
'habits': <Object>[],
|
||||
'sessions': <Object>[],
|
||||
'reflections': <Object>[],
|
||||
});
|
||||
|
||||
expect(state.anchorDateKey, isNotEmpty);
|
||||
expect(state.toJson()['version'], 2);
|
||||
});
|
||||
}
|
||||
+388
-62
@@ -1,9 +1,13 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:drift/native.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:focus_seed/src/app/focus_seed_app.dart';
|
||||
import 'package:focus_seed/src/data/focus_seed_store.dart';
|
||||
import 'package:focus_seed/src/data/local/focus_seed_database.dart';
|
||||
import 'package:focus_seed/src/models/app_state.dart';
|
||||
import 'package:focus_seed/src/models/focus_session.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
void main() {
|
||||
@@ -16,45 +20,352 @@ void main() {
|
||||
expect(focusSeedDefaultLocale(), const Locale('zh'));
|
||||
});
|
||||
|
||||
testWidgets('Today guides the user from intention to focus', (tester) async {
|
||||
test('welcome art uses managed onboarding assets', () {
|
||||
final welcomeSource =
|
||||
File(
|
||||
'lib/src/features/onboarding/welcome_screen.dart',
|
||||
).readAsStringSync();
|
||||
final landingSource =
|
||||
File(
|
||||
'lib/src/features/onboarding/landing_setup_screen.dart',
|
||||
).readAsStringSync();
|
||||
final recordsSource =
|
||||
File('lib/src/features/records/records_screen.dart').readAsStringSync();
|
||||
|
||||
expect(welcomeSource, contains('Image.asset'));
|
||||
expect(welcomeSource, contains('AppAssets.welcomePath'));
|
||||
expect(welcomeSource, contains('AppAssets.focusBlock'));
|
||||
expect(welcomeSource, contains('AppAssets.reflectionNote'));
|
||||
expect(landingSource, contains('AppAssets.landingFocusStart'));
|
||||
expect(recordsSource, contains('AppAssets.recordsEmpty'));
|
||||
expect(welcomeSource, isNot(contains('KineticIllustration')));
|
||||
expect(welcomeSource, isNot(contains('SingleChildScrollView')));
|
||||
expect(welcomeSource, isNot(contains('Parallax')));
|
||||
expect(landingSource, isNot(contains('KineticIllustration')));
|
||||
expect(recordsSource, isNot(contains('KineticIllustration')));
|
||||
expect(welcomeSource, isNot(contains('SeedIllustration')));
|
||||
expect(welcomeSource, isNot(contains('MiniTerrainMark')));
|
||||
});
|
||||
|
||||
testWidgets('Today keeps the core loop free of saved plan noise', (
|
||||
tester,
|
||||
) async {
|
||||
await pumpFocusSeedApp(tester, locale: const Locale('en'));
|
||||
|
||||
expect(find.text('Focus Seed'), findsOneWidget);
|
||||
expect(find.text("TODAY'S FOCUS"), findsOneWidget);
|
||||
expect(find.text("Set today's focus"), findsOneWidget);
|
||||
expect(find.text('Start 10 min'), findsWidgets);
|
||||
expect(find.text('Name today\'s focus'), findsOneWidget);
|
||||
expect(find.text('Pick a focus plan'), findsNothing);
|
||||
expect(find.text('Deep work sprint'), findsNothing);
|
||||
});
|
||||
|
||||
await tester.tap(find.text("Name today's focus"));
|
||||
testWidgets('first launch shows welcome before Today', (tester) async {
|
||||
final database = FocusSeedDatabase(NativeDatabase.memory());
|
||||
addTearDown(database.close);
|
||||
await tester.pumpWidget(
|
||||
FocusSeedApp(
|
||||
locale: const Locale('en'),
|
||||
focusSeedStore: FocusSeedStore(database: database),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 300));
|
||||
|
||||
expect(find.text('Plant just one thing each day.'), findsOneWidget);
|
||||
expect(find.text('Continue'), findsOneWidget);
|
||||
expect(find.byType(Image), findsWidgets);
|
||||
|
||||
await tester.tap(find.text('Continue'));
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 400));
|
||||
expect(find.text('Protect 10 minutes first.'), findsOneWidget);
|
||||
expect(find.byType(Image), findsWidgets);
|
||||
|
||||
await tester.tap(find.text('Continue'));
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 400));
|
||||
expect(find.text('Leave one progress note.'), findsOneWidget);
|
||||
expect(find.byType(Image), findsWidgets);
|
||||
|
||||
await tester.tap(find.text('Set today\'s focus'));
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 700));
|
||||
|
||||
expect(find.text('Start with 10 minutes.'), findsOneWidget);
|
||||
expect(find.text('Write down what you will do'), findsOneWidget);
|
||||
expect(find.text('What do you want to do first?'), findsNothing);
|
||||
|
||||
await tester.tap(find.text('Write down what you will do'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('What do you want to do first?'), findsOneWidget);
|
||||
|
||||
await tester.ensureVisible(find.text('Start 10 minutes'));
|
||||
await tester.pumpAndSettle();
|
||||
final enterTodayButton = find.ancestor(
|
||||
of: find.text('Start 10 minutes'),
|
||||
matching: find.byWidgetPredicate((widget) => widget is ButtonStyleButton),
|
||||
);
|
||||
expect(enterTodayButton, findsOneWidget);
|
||||
expect(
|
||||
(tester.widget(enterTodayButton) as ButtonStyleButton).onPressed,
|
||||
isNotNull,
|
||||
);
|
||||
await tester.tap(enterTodayButton);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Deep work in progress'), findsOneWidget);
|
||||
expect(find.text('Start this session first'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('landing setup can save typed action before starting', (
|
||||
tester,
|
||||
) async {
|
||||
SharedPreferences.setMockInitialValues({
|
||||
'focus_seed_onboarding_complete': true,
|
||||
'focus_seed_landing_setup_complete': false,
|
||||
});
|
||||
final database = FocusSeedDatabase(NativeDatabase.memory());
|
||||
addTearDown(database.close);
|
||||
await tester.pumpWidget(
|
||||
FocusSeedApp(
|
||||
locale: const Locale('en'),
|
||||
focusSeedStore: FocusSeedStore(database: database),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.text('Write down what you will do'));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.enterText(
|
||||
find.byType(TextField),
|
||||
'Finish the launch checklist',
|
||||
);
|
||||
await tester.ensureVisible(find.text('Start 10 minutes'));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.text('Start 10 minutes'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(
|
||||
find.text('Today\'s focus: Finish the launch checklist'),
|
||||
findsOneWidget,
|
||||
);
|
||||
expect(find.text('10 min'), findsWidgets);
|
||||
});
|
||||
|
||||
testWidgets('finished onboarding restores landing until it is completed', (
|
||||
tester,
|
||||
) async {
|
||||
SharedPreferences.setMockInitialValues({
|
||||
'focus_seed_onboarding_complete': true,
|
||||
'focus_seed_landing_setup_complete': false,
|
||||
});
|
||||
final database = FocusSeedDatabase(NativeDatabase.memory());
|
||||
addTearDown(database.close);
|
||||
await tester.pumpWidget(
|
||||
FocusSeedApp(
|
||||
locale: const Locale('en'),
|
||||
focusSeedStore: FocusSeedStore(database: database),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Start with 10 minutes.'), findsOneWidget);
|
||||
expect(find.text('Focus Seed'), findsNothing);
|
||||
|
||||
await tester.ensureVisible(find.text('Enter Today'));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.text('Enter Today'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Focus Seed'), findsOneWidget);
|
||||
expect(find.text('Start with 10 minutes.'), findsNothing);
|
||||
|
||||
await tester.pumpWidget(
|
||||
FocusSeedApp(
|
||||
locale: const Locale('en'),
|
||||
focusSeedStore: FocusSeedStore(database: database),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Focus Seed'), findsOneWidget);
|
||||
expect(find.text('Start with 10 minutes.'), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('bottom navigation exposes Focus, Records, and Me', (
|
||||
tester,
|
||||
) async {
|
||||
await pumpFocusSeedApp(tester, locale: const Locale('en'));
|
||||
|
||||
await tester.tap(_navDestination('Focus'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('Ready when you are'), findsOneWidget);
|
||||
|
||||
await tester.tap(_navDestination('Records'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('Records'), findsWidgets);
|
||||
expect(find.text('No progress records yet'), findsOneWidget);
|
||||
|
||||
await tester.tap(_navDestination('Me'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('Me'), findsWidgets);
|
||||
expect(find.text('Preferences'), findsWidgets);
|
||||
|
||||
await tester.tap(_navDestination('Today'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('Name today\'s focus'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('V1 action loop records a session and capture', (tester) async {
|
||||
await pumpFocusSeedApp(tester, locale: const Locale('en'));
|
||||
|
||||
final startButton = find.ancestor(
|
||||
of: find.text('Start 10 min').last,
|
||||
matching: find.byWidgetPredicate((widget) => widget is ButtonStyleButton),
|
||||
);
|
||||
await tester.ensureVisible(startButton);
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(startButton);
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('Deep work in progress'), findsOneWidget);
|
||||
|
||||
await tester.tap(find.text('Pause'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('Paused - your place is kept'), findsOneWidget);
|
||||
|
||||
await tester.tap(find.text('Resume'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('Deep work in progress'), findsOneWidget);
|
||||
|
||||
await tester.tap(find.text('Complete session'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('Session complete'), findsOneWidget);
|
||||
expect(find.text('Write progress note'), findsOneWidget);
|
||||
|
||||
await tester.tap(find.text('Write progress note'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('Leave one progress note'), findsOneWidget);
|
||||
await tester.enterText(find.byType(TextField).last, 'Finished one pass.');
|
||||
await tester.tap(find.text('Save note'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(_navDestination('Records'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('Records'), findsWidgets);
|
||||
expect(find.text('Finished one pass.'), findsOneWidget);
|
||||
expect(find.text('No progress records yet'), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('Records can backfill reflection for a past daily record', (
|
||||
tester,
|
||||
) async {
|
||||
final database = FocusSeedDatabase(NativeDatabase.memory());
|
||||
addTearDown(database.close);
|
||||
final store = FocusSeedStore(database: database);
|
||||
final now = DateTime.now();
|
||||
final yesterday = now.subtract(const Duration(days: 1));
|
||||
await store.save(
|
||||
AppState(
|
||||
anchor: '',
|
||||
anchorDateKey: '',
|
||||
habits: const [],
|
||||
sessions: [
|
||||
FocusSession(
|
||||
id: 'session-yesterday',
|
||||
minutes: 10,
|
||||
actualSeconds: 600,
|
||||
completedAt: yesterday,
|
||||
intention: 'Draft launch notes',
|
||||
),
|
||||
],
|
||||
reflections: const [],
|
||||
),
|
||||
);
|
||||
await tester.pumpWidget(
|
||||
FocusSeedApp(
|
||||
locale: const Locale('en'),
|
||||
focusSeedStore: store,
|
||||
showWelcomeOnFirstRun: false,
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(_navDestination('Records'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Draft launch notes'), findsWidgets);
|
||||
expect(find.text('No progress note yet'), findsWidgets);
|
||||
expect(find.text('Write one sentence to close today'), findsOneWidget);
|
||||
expect(find.text('Continue this'), findsNothing);
|
||||
|
||||
final reflectionButton = find.ancestor(
|
||||
of: find.text('Write one sentence to close today'),
|
||||
matching: find.byWidgetPredicate((widget) => widget is ButtonStyleButton),
|
||||
);
|
||||
await tester.ensureVisible(reflectionButton);
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(reflectionButton);
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('Leave one progress note'), findsOneWidget);
|
||||
expect(find.text('TODAY\'S FOCUS'), findsWidgets);
|
||||
expect(find.text('Draft launch notes'), findsWidgets);
|
||||
await tester.enterText(find.byType(TextField).last, 'Next: polish copy.');
|
||||
final saveNoteButton = find.ancestor(
|
||||
of: find.text('Save note'),
|
||||
matching: find.byWidgetPredicate((widget) => widget is ButtonStyleButton),
|
||||
);
|
||||
await tester.ensureVisible(saveNoteButton);
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(saveNoteButton);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Next: polish copy.'), findsOneWidget);
|
||||
expect(find.text('Write one sentence to close today'), findsNothing);
|
||||
expect(find.text('Continue this'), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('settings manages local data without JSON tools', (tester) async {
|
||||
await pumpFocusSeedApp(tester, locale: const Locale('en'));
|
||||
|
||||
await tester.tap(_navDestination('Me'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
var meScroll = find.byType(Scrollable).first;
|
||||
expect(find.text('Personal rhythm'), findsNothing);
|
||||
expect(find.text('Saved plans'), findsNothing);
|
||||
await tester.scrollUntilVisible(
|
||||
find.text('Theme, language, and default focus'),
|
||||
180,
|
||||
scrollable: meScroll,
|
||||
);
|
||||
final preferencesRow = find.ancestor(
|
||||
of: find.text('Theme, language, and default focus'),
|
||||
matching: find.byType(InkWell),
|
||||
);
|
||||
expect(preferencesRow, findsOneWidget);
|
||||
await tester.tap(preferencesRow);
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('Default focus'), findsOneWidget);
|
||||
expect(find.text('10 min'), findsWidgets);
|
||||
expect(find.text('1'), findsOneWidget);
|
||||
expect(find.text('5'), findsOneWidget);
|
||||
expect(find.text('45'), findsOneWidget);
|
||||
await tester.tap(find.byIcon(Icons.add_rounded).last);
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('Custom length'), findsOneWidget);
|
||||
await tester.enterText(find.byType(TextField), '7');
|
||||
await tester.tap(find.text('Save'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Finish the launch checklist'), findsOneWidget);
|
||||
expect(find.text('Start 25 min'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('bottom navigation exposes Journey and Me', (tester) async {
|
||||
await pumpFocusSeedApp(tester, locale: const Locale('en'));
|
||||
|
||||
await tester.tap(find.text('Journey'));
|
||||
expect(find.text('7 min'), findsOneWidget);
|
||||
await tester.pageBack();
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('Progress'), findsOneWidget);
|
||||
|
||||
await tester.tap(find.text('Me'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('Your garden'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('Me manages local data without JSON tools', (tester) async {
|
||||
await pumpFocusSeedApp(tester, locale: const Locale('en'));
|
||||
|
||||
await tester.tap(find.text('Me'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final meScroll = find.byType(Scrollable).first;
|
||||
meScroll = find.byType(Scrollable).first;
|
||||
await tester.scrollUntilVisible(
|
||||
find.text('Local records'),
|
||||
180,
|
||||
scrollable: meScroll,
|
||||
);
|
||||
expect(find.text('Local records'), findsOneWidget);
|
||||
expect(find.text('Clear today'), findsOneWidget);
|
||||
await tester.scrollUntilVisible(
|
||||
@@ -67,35 +378,17 @@ void main() {
|
||||
expect(find.text('Import JSON'), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('Journey opens history detail with empty states', (tester) async {
|
||||
testWidgets('Records shows empty focus and reflection states', (
|
||||
tester,
|
||||
) async {
|
||||
await pumpFocusSeedApp(tester, locale: const Locale('en'));
|
||||
|
||||
await tester.tap(find.text('Journey'));
|
||||
await tester.tap(_navDestination('Records'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final journeyScroll = find.byType(Scrollable).first;
|
||||
await tester.scrollUntilVisible(
|
||||
find.text('7-day rhythm'),
|
||||
180,
|
||||
scrollable: journeyScroll,
|
||||
);
|
||||
expect(find.text('7-day rhythm'), findsOneWidget);
|
||||
await tester.scrollUntilVisible(
|
||||
find.text('Record library'),
|
||||
180,
|
||||
scrollable: journeyScroll,
|
||||
);
|
||||
expect(find.text('Record library'), findsOneWidget);
|
||||
|
||||
await tester.tap(find.text('Record library'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('History'), findsOneWidget);
|
||||
expect(find.text('No focus sessions yet'), findsOneWidget);
|
||||
|
||||
await tester.tap(find.text('Notes'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('No notes yet'), findsOneWidget);
|
||||
expect(find.text('Records'), findsWidgets);
|
||||
expect(find.text('No progress records yet'), findsOneWidget);
|
||||
expect(find.text('Back to Today'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('Today remains usable at narrow width and 200 percent text', (
|
||||
@@ -121,16 +414,16 @@ void main() {
|
||||
await pumpFocusSeedApp(tester);
|
||||
|
||||
expect(find.text('Focus Seed'), findsOneWidget);
|
||||
expect(find.text('\u4eca\u65e5\u91cd\u70b9'), findsOneWidget);
|
||||
expect(find.text('\u8bbe\u7f6e\u4eca\u65e5\u91cd\u70b9'), findsOneWidget);
|
||||
await tester.scrollUntilVisible(
|
||||
find.text('\u8ba1\u5212\u4e00\u4e2a\u660e\u786e\u4efb\u52a1'),
|
||||
180,
|
||||
);
|
||||
expect(
|
||||
find.text('\u8ba1\u5212\u4e00\u4e2a\u660e\u786e\u4efb\u52a1'),
|
||||
find.text('\u5199\u4e0b\u4eca\u5929\u7684\u91cd\u70b9'),
|
||||
findsOneWidget,
|
||||
);
|
||||
expect(find.text('\u5f00\u59cb 10 \u5206\u949f'), findsWidgets);
|
||||
expect(
|
||||
find.text('\u9009\u62e9\u4e00\u4e2a\u4e13\u6ce8\u8ba1\u5212'),
|
||||
findsNothing,
|
||||
);
|
||||
expect(find.text('\u6df1\u5ea6\u5de5\u4f5c'), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('development language switch changes the active locale', (
|
||||
@@ -138,15 +431,36 @@ void main() {
|
||||
) async {
|
||||
await pumpFocusSeedApp(tester);
|
||||
|
||||
await tester.tap(find.text('\u6211\u7684'));
|
||||
await tester.tap(_navDestination('\u6211\u7684'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final settingsScroll = find.byType(Scrollable).first;
|
||||
await tester.scrollUntilVisible(
|
||||
find.text('\u4e3b\u9898\u3001\u8bed\u8a00\u548c\u9ed8\u8ba4\u4e13\u6ce8'),
|
||||
180,
|
||||
scrollable: settingsScroll,
|
||||
);
|
||||
final preferencesRow = find.ancestor(
|
||||
of: find.text(
|
||||
'\u4e3b\u9898\u3001\u8bed\u8a00\u548c\u9ed8\u8ba4\u4e13\u6ce8',
|
||||
),
|
||||
matching: find.byType(InkWell),
|
||||
);
|
||||
await tester.tap(preferencesRow);
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('\u8bed\u8a00'), findsOneWidget);
|
||||
await tester.tap(find.byIcon(Icons.language_rounded));
|
||||
final languageRow = find.ancestor(
|
||||
of: find.text('\u8bed\u8a00'),
|
||||
matching: find.byType(InkWell),
|
||||
);
|
||||
expect(languageRow, findsOneWidget);
|
||||
await tester.tap(languageRow);
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.text('English'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Language'), findsOneWidget);
|
||||
expect(find.text('Development only'), findsOneWidget);
|
||||
expect(find.text('English'), findsOneWidget);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -157,7 +471,19 @@ Future<void> pumpFocusSeedApp(WidgetTester tester, {Locale? locale}) async {
|
||||
FocusSeedApp(
|
||||
locale: locale,
|
||||
focusSeedStore: FocusSeedStore(database: database),
|
||||
showWelcomeOnFirstRun: false,
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
|
||||
Finder _navDestination(String label) {
|
||||
final key = switch (label) {
|
||||
'Today' || '\u4eca\u65e5' => 'today',
|
||||
'Focus' || '\u4e13\u6ce8' => 'focus',
|
||||
'Records' || '\u8bb0\u5f55' => 'records',
|
||||
'Me' || '\u6211\u7684' => 'me',
|
||||
_ => label.toLowerCase(),
|
||||
};
|
||||
return find.byKey(ValueKey('focus-seed-nav-$key'));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user