chore: Halkyon Learn moves into apps/learn
Its server, client, app and curator, and the tools that write its assets (maps, Anki fixtures, icons), as they are; paths are fixed in the commits that follow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
11abd9afce
commit
649499be23
3 changed files with 0 additions and 0 deletions
|
|
@ -0,0 +1,226 @@
|
|||
import 'package:clock/clock.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/card/data/card_repository.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/card/model/card_content.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/home/bloc/home_bloc.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/home/model/home_layout.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/home/model/study_goal.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/learning_path/data/path_progress_repository.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/learning_path/model/progress.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/settings/data/settings_repository.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/study/data/review_repository.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/study/model/rating.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/studyset/data/studyset_repository.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/studyset/model/studyset_content.dart';
|
||||
|
||||
import '../../../support/fake_card_gateway.dart';
|
||||
import '../../../support/fake_path_answer_gateway.dart';
|
||||
import '../../../support/fake_review_gateway.dart';
|
||||
import '../../../support/fake_settings_gateway.dart';
|
||||
import '../../../support/fake_studyset_gateway.dart';
|
||||
|
||||
void main() {
|
||||
late StudysetRepository studysets;
|
||||
late CardRepository cards;
|
||||
late ReviewRepository reviews;
|
||||
late SettingsRepository settings;
|
||||
late PathProgressRepository pathProgress;
|
||||
late HomeBloc bloc;
|
||||
// A Wednesday afternoon, local time.
|
||||
final now = DateTime(2026, 10, 7, 15);
|
||||
final today = DateTime(2026, 10, 7);
|
||||
|
||||
setUp(() {
|
||||
studysets = StudysetRepository(FakeStudysetGateway());
|
||||
cards = CardRepository(FakeCardGateway());
|
||||
reviews = ReviewRepository(FakeReviewGateway());
|
||||
settings = SettingsRepository(FakeSettingsGateway());
|
||||
pathProgress = PathProgressRepository(FakePathAnswerGateway());
|
||||
});
|
||||
tearDown(() async {
|
||||
await bloc.close();
|
||||
await studysets.dispose();
|
||||
await cards.dispose();
|
||||
await reviews.dispose();
|
||||
await settings.dispose();
|
||||
await pathProgress.dispose();
|
||||
});
|
||||
|
||||
CardContent card() =>
|
||||
CardContent(front: RichContent.empty(), back: RichContent.empty());
|
||||
|
||||
void start() =>
|
||||
bloc = HomeBloc(studysets, cards, reviews, settings, pathProgress)
|
||||
..add(const HomeEventStarted());
|
||||
|
||||
Future<HomeStateLoaded> showing(bool Function(HomeStateLoaded) test) async =>
|
||||
await bloc.stream.firstWhere((s) => s is HomeStateLoaded && test(s))
|
||||
as HomeStateLoaded;
|
||||
|
||||
test('a step of a path done counts as activity, and keeps the '
|
||||
'streak', () async {
|
||||
await withClock(Clock.fixed(now), () async {
|
||||
await withClock(
|
||||
Clock.fixed(now.subtract(const Duration(days: 1))),
|
||||
() => pathProgress.record('step-1', AnswerOutcome.continued),
|
||||
);
|
||||
start();
|
||||
final yesterday = await showing((s) => s.streak == 1);
|
||||
expect(yesterday.activity.on(today), 0);
|
||||
|
||||
await pathProgress.record('step-2', AnswerOutcome.continued);
|
||||
await pathProgress.record('step-3', AnswerOutcome.wrong);
|
||||
|
||||
final both = await showing((s) => s.streak == 2);
|
||||
expect(both.activity.on(today), 1);
|
||||
expect(both.goalProgress, 0);
|
||||
});
|
||||
});
|
||||
|
||||
test('counts ratings and cards written, keeps the streak and the goal, '
|
||||
'and follows new ratings', () async {
|
||||
await withClock(Clock.fixed(now), () async {
|
||||
final japanese = await studysets.create(
|
||||
StudysetContent.titled('Japanese'),
|
||||
);
|
||||
final neko = await cards.create(japanese, card());
|
||||
await reviews.record(
|
||||
cardId: neko,
|
||||
rating: Rating.good,
|
||||
reviewedAt: now.subtract(const Duration(days: 1)),
|
||||
duration: Duration.zero,
|
||||
);
|
||||
await settings.update(
|
||||
(s) => s.withGoal(const StudyGoal(period: GoalPeriod.day, target: 5)),
|
||||
);
|
||||
start();
|
||||
|
||||
// Written today, rated yesterday: two days in a row.
|
||||
final first = await showing((s) => s.goal != null);
|
||||
expect(first.streak, 2);
|
||||
expect(first.activity.on(today), 1);
|
||||
expect(first.goalProgress, 0);
|
||||
|
||||
await reviews.record(
|
||||
cardId: neko,
|
||||
rating: Rating.easy,
|
||||
reviewedAt: now,
|
||||
duration: Duration.zero,
|
||||
);
|
||||
final rated = await showing((s) => s.goalProgress == 1);
|
||||
expect(rated.activity.on(today), 2);
|
||||
});
|
||||
});
|
||||
|
||||
test('imported cards are no writing of the day', () async {
|
||||
await withClock(Clock.fixed(now), () async {
|
||||
final japanese = await studysets.create(
|
||||
StudysetContent.titled('Japanese'),
|
||||
);
|
||||
await cards.createAll(japanese, [card(), card()], origin: 'anki');
|
||||
await cards.create(japanese, card());
|
||||
start();
|
||||
|
||||
final state = await showing((s) => true);
|
||||
expect(state.activity.on(today), 1);
|
||||
});
|
||||
});
|
||||
|
||||
test('an import alone starts the activity, without studying', () async {
|
||||
await withClock(Clock.fixed(now), () async {
|
||||
final japanese = await studysets.create(
|
||||
StudysetContent.titled('Japanese'),
|
||||
);
|
||||
await cards.createAll(japanese, [card()], origin: 'anki');
|
||||
start();
|
||||
|
||||
final state = await showing((s) => true);
|
||||
expect(state.activity.start, today);
|
||||
expect(state.activity.on(today), 0);
|
||||
});
|
||||
});
|
||||
|
||||
test('cards fall due on their day, but those resting in an archived '
|
||||
'studyset', () async {
|
||||
await withClock(Clock.fixed(now), () async {
|
||||
final japanese = await studysets.create(
|
||||
StudysetContent.titled('Japanese'),
|
||||
);
|
||||
final german = await studysets.create(StudysetContent.titled('German'));
|
||||
for (final studyset in [japanese, german]) {
|
||||
final id = await cards.create(studyset, card());
|
||||
await reviews.record(
|
||||
cardId: id,
|
||||
rating: Rating.easy,
|
||||
reviewedAt: now,
|
||||
duration: Duration.zero,
|
||||
);
|
||||
}
|
||||
await studysets.archive(german);
|
||||
start();
|
||||
|
||||
final state = await showing((s) => s.due.isNotEmpty);
|
||||
expect(state.due.values.fold(0, (a, b) => a + b), 1);
|
||||
});
|
||||
});
|
||||
|
||||
test('a rest day keeps the streak, and toggles off again', () async {
|
||||
await withClock(Clock.fixed(now), () async {
|
||||
final japanese = await studysets.create(
|
||||
StudysetContent.titled('Japanese'),
|
||||
);
|
||||
final neko = await cards.create(japanese, card());
|
||||
await reviews.record(
|
||||
cardId: neko,
|
||||
rating: Rating.good,
|
||||
reviewedAt: now.subtract(const Duration(days: 2)),
|
||||
duration: Duration.zero,
|
||||
);
|
||||
start();
|
||||
// Written today, rated two days ago: yesterday breaks it.
|
||||
expect((await showing((s) => true)).streak, 1);
|
||||
|
||||
final yesterday = DateTime(2026, 10, 6);
|
||||
bloc.add(HomeEventRestDayToggled(yesterday));
|
||||
expect((await showing((s) => s.restDays.isNotEmpty)).streak, 3);
|
||||
|
||||
bloc.add(HomeEventRestDayToggled(yesterday));
|
||||
expect((await showing((s) => s.restDays.isEmpty)).streak, 1);
|
||||
});
|
||||
});
|
||||
|
||||
test('saves the goal and the layout in the settings', () async {
|
||||
start();
|
||||
await showing((s) => true);
|
||||
|
||||
bloc
|
||||
..add(
|
||||
const HomeEventGoalSet(StudyGoal(period: GoalPeriod.week, target: 50)),
|
||||
)
|
||||
..add(
|
||||
const HomeEventLayoutChanged(
|
||||
HomeLayout(
|
||||
order: [
|
||||
HomeWidget.week,
|
||||
HomeWidget.goal,
|
||||
HomeWidget.pomodoro,
|
||||
HomeWidget.activity,
|
||||
HomeWidget.deadlines,
|
||||
],
|
||||
hidden: {
|
||||
HomeWidget.pomodoro,
|
||||
HomeWidget.activity,
|
||||
HomeWidget.deadlines,
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
final state = await showing(
|
||||
(s) => s.goal != null && s.layout.hidden.isNotEmpty,
|
||||
);
|
||||
|
||||
expect(state.layout.shown, [HomeWidget.week, HomeWidget.goal]);
|
||||
expect((await settings.read()).goal?.target, 50);
|
||||
});
|
||||
}
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/home/model/study_activity.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/home/model/study_goal.dart';
|
||||
|
||||
void main() {
|
||||
// A Wednesday, in the afternoon, local time.
|
||||
final now = DateTime(2026, 10, 7, 15);
|
||||
DateTime day(int d) => DateTime(2026, 10, d);
|
||||
DateTime at(int d, [int hour = 10]) => DateTime(2026, 10, d, hour);
|
||||
|
||||
StudyActivity studied(List<int> days) =>
|
||||
StudyActivity.of(ratings: [for (final d in days) at(d)], written: []);
|
||||
|
||||
test('counts ratings and cards written by the local day they fell on', () {
|
||||
final activity = StudyActivity.of(
|
||||
ratings: [at(5, 9), at(5, 23)],
|
||||
written: [at(6)],
|
||||
);
|
||||
|
||||
expect(activity.on(day(5)), 2);
|
||||
expect(activity.on(day(6)), 1);
|
||||
expect(activity.studiedOn(day(4)), isFalse);
|
||||
});
|
||||
|
||||
test('starts on the first day anything happened, imports included, which '
|
||||
"don't count as studying", () {
|
||||
final activity = StudyActivity.of(
|
||||
ratings: [at(6)],
|
||||
written: [at(5)],
|
||||
imported: [at(4, 23)],
|
||||
);
|
||||
|
||||
expect(activity.start, day(4));
|
||||
expect(activity.studiedOn(day(4)), isFalse);
|
||||
expect(StudyActivity.none.start, isNull);
|
||||
});
|
||||
|
||||
group('The streak', () {
|
||||
test('runs up to today once studied today', () {
|
||||
expect(studyStreak(studied([5, 6, 7]), {}, now), 3);
|
||||
});
|
||||
|
||||
test('runs up to yesterday while today is still open', () {
|
||||
expect(studyStreak(studied([5, 6]), {}, now), 2);
|
||||
});
|
||||
|
||||
test('is broken by a day without studying or resting', () {
|
||||
expect(studyStreak(studied([4, 6, 7]), {}, now), 2);
|
||||
expect(studyStreak(studied([4, 5]), {}, now), 0);
|
||||
});
|
||||
|
||||
test('keeps going over a rest day, which counts', () {
|
||||
expect(studyStreak(studied([4, 6, 7]), {day(5)}, now), 4);
|
||||
});
|
||||
});
|
||||
|
||||
test('a week runs Monday to Sunday, each day as it stands', () {
|
||||
final week = weekOf(day(7), studied([5, 7]), {day(6)}, now);
|
||||
|
||||
expect(week.map((each) => each.day.day), [5, 6, 7, 8, 9, 10, 11]);
|
||||
expect(week.map((each) => each.state), [
|
||||
DayState.studied,
|
||||
DayState.rested,
|
||||
DayState.studied,
|
||||
DayState.upcoming,
|
||||
DayState.upcoming,
|
||||
DayState.upcoming,
|
||||
DayState.upcoming,
|
||||
]);
|
||||
expect(
|
||||
weekOf(day(7), studied([5]), {}, now).take(3).map((each) => each.state),
|
||||
[DayState.studied, DayState.missed, DayState.today],
|
||||
);
|
||||
});
|
||||
|
||||
test('days before the first activity have nothing to miss; a rest day '
|
||||
'still shows', () {
|
||||
expect(
|
||||
weekOf(day(7), studied([6]), {}, now).take(3).map((each) => each.state),
|
||||
[DayState.notStarted, DayState.studied, DayState.today],
|
||||
);
|
||||
expect(
|
||||
weekOf(day(7), studied([]), {day(5)}, now).take(3).map((e) => e.state),
|
||||
[DayState.rested, DayState.notStarted, DayState.today],
|
||||
);
|
||||
});
|
||||
|
||||
test('heat starts at one card and darkens in coarse steps', () {
|
||||
expect([0, 1, 4, 5, 9, 10, 19, 20].map(heatLevel), [
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
2,
|
||||
2,
|
||||
3,
|
||||
3,
|
||||
4,
|
||||
]);
|
||||
});
|
||||
|
||||
test('cards fall due on their day; overdue ones today', () {
|
||||
expect(dueByDay([at(1), at(7), at(9), at(9, 20)], day(7)), {
|
||||
day(7): 2,
|
||||
day(9): 2,
|
||||
});
|
||||
});
|
||||
|
||||
test("a goal's progress counts the ratings of its day or week", () {
|
||||
final ratings = [at(4), at(5), at(6), at(7), at(7, 14)];
|
||||
|
||||
expect(
|
||||
goalProgress(
|
||||
const StudyGoal(period: GoalPeriod.day, target: 20),
|
||||
ratings,
|
||||
now,
|
||||
),
|
||||
2,
|
||||
);
|
||||
expect(
|
||||
goalProgress(
|
||||
const StudyGoal(period: GoalPeriod.week, target: 20),
|
||||
ratings,
|
||||
now,
|
||||
),
|
||||
4,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
@ -0,0 +1,270 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/card/data/card_repository.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/card/model/card_content.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/home/widget/goal_card.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/home/widget/streak.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/pomodoro/widget/pomodoro_card.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/study/data/review_repository.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/study/model/rating.dart';
|
||||
import 'package:halkyon_ui/halkyon_ui.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:lucide_icons_flutter/lucide_icons.dart';
|
||||
import 'package:material_ui/material_ui.dart';
|
||||
|
||||
import '../../../support/fake_card_gateway.dart';
|
||||
import '../../../support/fake_review_gateway.dart';
|
||||
import '../../../support/fake_settings_gateway.dart';
|
||||
import '../../../support/pump_app.dart';
|
||||
import '../../../support/test_dependencies.dart';
|
||||
|
||||
void main() {
|
||||
late FakeCardGateway cardGateway;
|
||||
late FakeReviewGateway reviewGateway;
|
||||
late FakeSettingsGateway settingsGateway;
|
||||
|
||||
setUp(() {
|
||||
cardGateway = FakeCardGateway();
|
||||
reviewGateway = FakeReviewGateway();
|
||||
settingsGateway = FakeSettingsGateway();
|
||||
});
|
||||
|
||||
Future<void> showHome(WidgetTester tester, {double width = 1200}) async {
|
||||
tester.view.physicalSize = Size(width, 1600);
|
||||
tester.view.devicePixelRatio = 1;
|
||||
addTearDown(tester.view.reset);
|
||||
await pumpApp(
|
||||
tester,
|
||||
testDependencies(
|
||||
cardGateway: cardGateway,
|
||||
reviewGateway: reviewGateway,
|
||||
settingsGateway: settingsGateway,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
int streak(WidgetTester tester) =>
|
||||
tester.widget<StreakChip>(find.byType(StreakChip)).streak;
|
||||
|
||||
/// Rates a new card now.
|
||||
Future<void> rateToday() async {
|
||||
final cards = CardRepository(cardGateway);
|
||||
addTearDown(cards.dispose);
|
||||
final id = await cards.create(
|
||||
'japanese',
|
||||
CardContent(front: RichContent.empty(), back: RichContent.empty()),
|
||||
);
|
||||
await ReviewRepository(reviewGateway).record(
|
||||
cardId: id,
|
||||
rating: Rating.good,
|
||||
reviewedAt: DateTime.now(),
|
||||
duration: Duration.zero,
|
||||
);
|
||||
}
|
||||
|
||||
testWidgets('cobalt washes down from the top, behind a clear app bar', (
|
||||
tester,
|
||||
) async {
|
||||
await showHome(tester);
|
||||
|
||||
expect(
|
||||
tester.widget<TopGradient>(find.byType(TopGradient)).color,
|
||||
Brand.primary,
|
||||
);
|
||||
expect(
|
||||
tester.widget<AppBar>(find.byType(AppBar)).backgroundColor,
|
||||
Colors.transparent,
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('opens with the goal first, then the Pomodoro timer, each on '
|
||||
'a card', (tester) async {
|
||||
await showHome(tester);
|
||||
|
||||
expect(
|
||||
tester.getRect(find.byType(GoalCard)).top,
|
||||
lessThan(tester.getRect(find.byType(PomodoroCard)).top),
|
||||
);
|
||||
expect(
|
||||
find.ancestor(of: find.byType(GoalCard), matching: find.byType(Card)),
|
||||
findsNothing,
|
||||
);
|
||||
expect(
|
||||
find.descendant(of: find.byType(GoalCard), matching: find.byType(Card)),
|
||||
findsOneWidget,
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('the streak sheet: the count inside the flame, the week as '
|
||||
'letters, and what keeps it going', (tester) async {
|
||||
await rateToday();
|
||||
await showHome(tester, width: 400);
|
||||
|
||||
await tester.tap(find.byType(StreakChip));
|
||||
await tester.pumpAndSettle();
|
||||
final sheet = find.byType(BottomSheet);
|
||||
|
||||
expect(
|
||||
find.descendant(of: sheet, matching: find.text('1 day streak')),
|
||||
findsOneWidget,
|
||||
);
|
||||
expect(
|
||||
find.descendant(of: sheet, matching: find.text('1')),
|
||||
findsOneWidget,
|
||||
);
|
||||
expect(
|
||||
find.descendant(of: sheet, matching: find.text('M')),
|
||||
findsOneWidget,
|
||||
);
|
||||
expect(
|
||||
find.descendant(
|
||||
of: sheet,
|
||||
matching: find.text('Come back tomorrow to keep your streak.'),
|
||||
),
|
||||
findsOneWidget,
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('the week names the day with its month short, and the '
|
||||
"heatmap's days say how much was done", (tester) async {
|
||||
await showHome(tester);
|
||||
final now = DateTime.now();
|
||||
|
||||
expect(
|
||||
find.text(
|
||||
'${DateFormat.EEEE('en_US').format(now)}, '
|
||||
'${DateFormat.MMMd('en_US').format(now)}',
|
||||
),
|
||||
findsOneWidget,
|
||||
);
|
||||
expect(
|
||||
find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is Tooltip &&
|
||||
(widget.message?.startsWith('0 activities · ') ?? false),
|
||||
),
|
||||
findsWidgets,
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('a card rated today lights the streak and the heatmap', (
|
||||
tester,
|
||||
) async {
|
||||
await rateToday();
|
||||
|
||||
await showHome(tester);
|
||||
|
||||
expect(streak(tester), 1);
|
||||
// The card written and the rating.
|
||||
expect(find.text('2 activities in the last year'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('a goal is set in its sheet and shows its progress', (
|
||||
tester,
|
||||
) async {
|
||||
await showHome(tester);
|
||||
|
||||
await tester.tap(find.text('Set a study goal'));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.text('Per week'));
|
||||
await tester.pump();
|
||||
await tester.tap(find.byTooltip('Increase'));
|
||||
await tester.pump();
|
||||
await tester.tap(find.text('Save goal'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Weekly goal'), findsOneWidget);
|
||||
// In steps of 5, as the study options.
|
||||
expect(find.text('0 of 25 cards this week'), findsOneWidget);
|
||||
expect(find.byIcon(LucideIcons.pencil), findsOneWidget);
|
||||
|
||||
await tester.tap(find.text('Weekly goal'));
|
||||
await tester.pumpAndSettle();
|
||||
// Removing sits below saving.
|
||||
expect(
|
||||
tester.getRect(find.text('Remove goal')).top,
|
||||
greaterThan(tester.getRect(find.text('Save goal')).bottom),
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('resting today keeps the streak', (tester) async {
|
||||
await showHome(tester);
|
||||
expect(streak(tester), 0);
|
||||
|
||||
await tester.tap(find.text('Rest day'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(streak(tester), 1);
|
||||
});
|
||||
|
||||
testWidgets("the rest day sits in the week's header, beside the day "
|
||||
'shown', (tester) async {
|
||||
await showHome(tester);
|
||||
|
||||
final restDay = tester.getRect(find.text('Rest day'));
|
||||
final previous = tester.getRect(find.byTooltip('Previous day'));
|
||||
expect(restDay.center.dy, closeTo(previous.center.dy, 2));
|
||||
expect(restDay.right, lessThan(previous.left));
|
||||
});
|
||||
|
||||
testWidgets('on a phone, the rest day is a snowflake button in the '
|
||||
"week's header", (tester) async {
|
||||
await showHome(tester, width: 360);
|
||||
expect(find.text('Rest day'), findsNothing);
|
||||
|
||||
final restDay = find.byTooltip('Rest day');
|
||||
expect(
|
||||
tester.getRect(restDay).center.dy,
|
||||
closeTo(tester.getRect(find.byTooltip('Previous day')).center.dy, 2),
|
||||
);
|
||||
await tester.tap(restDay);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(streak(tester), 1);
|
||||
});
|
||||
|
||||
testWidgets('a long press arranges the dashboard: a widget hidden stays '
|
||||
'hidden', (tester) async {
|
||||
await showHome(tester);
|
||||
|
||||
await tester.longPress(find.text('Set a study goal'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('Arrange widgets'), findsOneWidget);
|
||||
await tester.tap(find.byTooltip('Hide Daily goal'));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.text('Done'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Set a study goal'), findsNothing);
|
||||
expect(find.text('Your activity'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('with every widget hidden, Customise brings the arranging '
|
||||
'back', (tester) async {
|
||||
await showHome(tester);
|
||||
await tester.longPress(find.text('Set a study goal'));
|
||||
await tester.pumpAndSettle();
|
||||
for (final name in [
|
||||
'Daily goal',
|
||||
'Pomodoro',
|
||||
'PDF reading deadlines',
|
||||
'Week overview',
|
||||
'Activity heatmap',
|
||||
]) {
|
||||
await tester.tap(find.byTooltip('Hide $name'));
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
await tester.tap(find.text('Done'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('All widgets are hidden'), findsOneWidget);
|
||||
expect(
|
||||
find.text('Bring some back to fill your dashboard.'),
|
||||
findsOneWidget,
|
||||
);
|
||||
// Tests run in American English.
|
||||
await tester.tap(find.text('Customize'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('Arrange widgets'), findsOneWidget);
|
||||
});
|
||||
}
|
||||
Loading…
Reference in a new issue