halkyon/halkyon_learn_flutter/test/app/long_names_test.dart

182 lines
6.7 KiB
Dart
Raw Normal View History

fix: every text field has a limit, and long names no longer overflow A Pomodoro profile's name could be as long as one liked, and pushed the Home card's header beyond the window. Of 56 fields, only release notes and a licence's year had a limit. Limits by what a field holds (core/widget/text_limits.dart, decided 2026-10-11): a name shown in a menu or on a chip 40 (Pomodoro profiles, tags, a study button's label); a secondary title or a reading above characters 60; a title 120 (studysets, notes, paths, steps, PDFs, callouts, a video's caption, a licence's holder); an answer or item 200 (meanings, readings, glosses, options, categories, gaps, typed answers); a search 200; a part of a citation 500; a message 2000 (reports, replies, questions to Claude); a web address 2000; a passage with gaps or a formula 5000. The public profile and a published studyset's name take the instance's own limits (display name 30, handle 30, bio 500, links 2000, name 50), which the app didn't say before. Pasted flashcards to import and numbers have none. Fields count only from 80 % of their limit. Text can still be longer where it came from elsewhere (an older app, an import, a release), so the places showing it cut it short: the Pomodoro card's header and its profiles. A test shows names beyond every limit on Home, the library, a studyset's screen with its tabs and topics, the card browser with its tags, and a session, on a phone and a desktop: none overflows (it fails on the Pomodoro card as it was). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-11 14:06:18 +02:00
import 'package:flutter_test/flutter_test.dart';
import 'package:halkyon_learn_flutter/src/core/content/rich_content.dart';
import 'package:halkyon_learn_flutter/src/core/router/route_paths.dart';
import 'package:halkyon_learn_flutter/src/core/widget/text_limits.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/learning_path/data/learning_path_repository.dart';
import 'package:halkyon_learn_flutter/src/feature/learning_path/model/learning_path.dart';
import 'package:halkyon_learn_flutter/src/feature/note/data/note_repository.dart';
import 'package:halkyon_learn_flutter/src/feature/note/model/note_content.dart';
import 'package:halkyon_learn_flutter/src/feature/pomodoro/model/pomodoro_profile.dart';
import 'package:halkyon_learn_flutter/src/feature/settings/data/settings_repository.dart';
import 'package:halkyon_learn_flutter/src/feature/studyset/data/studyset_payload.dart';
import 'package:halkyon_learn_flutter/src/feature/studyset/model/studyset_content.dart';
import 'package:halkyon_learn_flutter/src/feature/tag/data/tag_repository.dart';
import 'package:material_ui/material_ui.dart';
import '../support/fake_card_gateway.dart';
import '../support/fake_learning_path_gateway.dart';
import '../support/fake_note_gateway.dart';
import '../support/fake_settings_gateway.dart';
import '../support/fake_studyset_gateway.dart';
import '../support/fake_tag_gateway.dart';
import '../support/pump_app.dart';
import '../support/test_dependencies.dart';
/// Names longer than the fields allow ([TextLimits]), as an older app, an
/// import or another person's release can bring them: every screen that
/// shows them cuts them short rather than overflowing, which fails a test.
void main() {
/// [length] characters of a name made of words, as long names are.
String long(String word, int length) =>
List.filled(length, word).join(' ').substring(0, length);
final studysetTitle = long('Japanese', TextLimits.title * 2);
final kicker = long('日本語', TextLimits.secondary * 2);
final topicTitle = long('Grammar', TextLimits.title * 2);
final tagName = long('Particles', TextLimits.name * 3);
final profileName = long('Deep focus', TextLimits.name * 3);
late FakeStudysetGateway studysets;
late FakeCardGateway cardGateway;
late FakeNoteGateway noteGateway;
late FakeLearningPathGateway pathGateway;
late FakeTagGateway tagGateway;
late FakeSettingsGateway settingsGateway;
setUp(() async {
studysets = FakeStudysetGateway()
..receiveRemoteChange(
'japanese',
encodeStudysetPayload(
StudysetContent(
title: studysetTitle,
secondaryTitle: kicker,
description: RichContent.plain(long('About', 400)),
),
),
)
..receiveRemoteChange(
'grammar',
encodeStudysetPayload(StudysetContent.titled(topicTitle)),
parentId: 'japanese',
)
..receiveRemoteChange(
'inner',
encodeStudysetPayload(StudysetContent.titled(topicTitle)),
parentId: 'grammar',
);
cardGateway = FakeCardGateway();
noteGateway = FakeNoteGateway();
pathGateway = FakeLearningPathGateway();
tagGateway = FakeTagGateway();
settingsGateway = FakeSettingsGateway();
final cards = CardRepository(cardGateway);
final cardId = await cards.create(
'japanese',
CardContent(
front: RichContent.plain(long('front', 300)),
back: RichContent.plain(long('back', 300)),
),
);
final tags = TagRepository(tagGateway, cardChanges: cards.changes);
await tags.setTagsOf(cardId, {await tags.create(tagName)});
await NoteRepository(noteGateway).create(
'japanese',
NoteContent(
title: long('Note', TextLimits.title * 2),
body: RichContent.empty(),
),
);
await LearningPathRepository(pathGateway).createPath(
'japanese',
LearningPathContent.titled(
long('Path', TextLimits.title * 2),
).withSecondaryTitle(long('Book', TextLimits.secondary * 2)),
);
final profile = PomodoroProfile(id: 'long', name: profileName);
await SettingsRepository(settingsGateway).update(
(settings) => settings.withPomodoro(
settings.pomodoro.withProfile(profile).withSelected(profile.id),
),
);
await Future.wait([tags.dispose(), cards.dispose()]);
});
for (final (window, width) in [('a phone', 360.0), ('a desktop', 1200.0)]) {
Future<void> show(WidgetTester tester, String location) async {
tester.view.physicalSize = Size(width, 800);
tester.view.devicePixelRatio = 1;
addTearDown(tester.view.reset);
await pumpApp(
tester,
testDependencies(
studysetGateway: studysets,
cardGateway: cardGateway,
noteGateway: noteGateway,
pathGateway: pathGateway,
tagGateway: tagGateway,
settingsGateway: settingsGateway,
),
location: location,
);
await tester.pump(const Duration(seconds: 1));
}
testWidgets('on $window, Home with a long Pomodoro profile', (
tester,
) async {
await show(tester, RoutePaths.home);
expect(find.textContaining('Deep focus'), findsWidgets);
await tester.tap(find.byTooltip(_profilesTooltip));
await tester.pumpAndSettle();
});
testWidgets('on $window, the library and a long studyset', (
tester,
) async {
await show(tester, RoutePaths.library);
expect(find.textContaining('Japanese'), findsWidgets);
});
2026-10-12 13:48:11 +02:00
testWidgets("on $window, a long studyset's screen, its parts and topics", (
fix: every text field has a limit, and long names no longer overflow A Pomodoro profile's name could be as long as one liked, and pushed the Home card's header beyond the window. Of 56 fields, only release notes and a licence's year had a limit. Limits by what a field holds (core/widget/text_limits.dart, decided 2026-10-11): a name shown in a menu or on a chip 40 (Pomodoro profiles, tags, a study button's label); a secondary title or a reading above characters 60; a title 120 (studysets, notes, paths, steps, PDFs, callouts, a video's caption, a licence's holder); an answer or item 200 (meanings, readings, glosses, options, categories, gaps, typed answers); a search 200; a part of a citation 500; a message 2000 (reports, replies, questions to Claude); a web address 2000; a passage with gaps or a formula 5000. The public profile and a published studyset's name take the instance's own limits (display name 30, handle 30, bio 500, links 2000, name 50), which the app didn't say before. Pasted flashcards to import and numbers have none. Fields count only from 80 % of their limit. Text can still be longer where it came from elsewhere (an older app, an import, a release), so the places showing it cut it short: the Pomodoro card's header and its profiles. A test shows names beyond every limit on Home, the library, a studyset's screen with its tabs and topics, the card browser with its tags, and a session, on a phone and a desktop: none overflows (it fails on the Pomodoro card as it was). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-11 14:06:18 +02:00
tester,
) async {
await show(tester, RoutePaths.studyset('grammar'));
2026-10-12 13:48:11 +02:00
for (final part in ['Study plan', 'Material']) {
await openPart(tester, part);
fix: every text field has a limit, and long names no longer overflow A Pomodoro profile's name could be as long as one liked, and pushed the Home card's header beyond the window. Of 56 fields, only release notes and a licence's year had a limit. Limits by what a field holds (core/widget/text_limits.dart, decided 2026-10-11): a name shown in a menu or on a chip 40 (Pomodoro profiles, tags, a study button's label); a secondary title or a reading above characters 60; a title 120 (studysets, notes, paths, steps, PDFs, callouts, a video's caption, a licence's holder); an answer or item 200 (meanings, readings, glosses, options, categories, gaps, typed answers); a search 200; a part of a citation 500; a message 2000 (reports, replies, questions to Claude); a web address 2000; a passage with gaps or a formula 5000. The public profile and a published studyset's name take the instance's own limits (display name 30, handle 30, bio 500, links 2000, name 50), which the app didn't say before. Pasted flashcards to import and numbers have none. Fields count only from 80 % of their limit. Text can still be longer where it came from elsewhere (an older app, an import, a release), so the places showing it cut it short: the Pomodoro card's header and its profiles. A test shows names beyond every limit on Home, the library, a studyset's screen with its tabs and topics, the card browser with its tags, and a session, on a phone and a desktop: none overflows (it fails on the Pomodoro card as it was). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-11 14:06:18 +02:00
}
if (width < 840) {
await tester.tap(find.byTooltip('Show topics'));
await tester.pumpAndSettle();
}
});
testWidgets('on $window, the card browser with a long tag', (
tester,
) async {
await show(tester, RoutePaths.cardBrowser('japanese'));
await tester.tap(find.byTooltip('Detail'));
await tester.pumpAndSettle();
await tester.tap(find.byTooltip('Select'));
await tester.pumpAndSettle();
await tester.tap(find.byTooltip('More'));
await tester.pumpAndSettle();
await tester.tap(find.text('Manage tags'));
await tester.pumpAndSettle();
});
testWidgets('on $window, studying a long studyset', (tester) async {
await show(tester, RoutePaths.study('japanese'));
});
}
}
/// The tooltip of the Pomodoro card's profiles button.
const _profilesTooltip = 'Pomodoro profiles';