feat(app): choose a studyset's licence, its year and holder

The studyset sheet gains Licence, as before: "Help me choose" asks three
questions (keep any rights? commercial use? adaptations, alike?) and
recommends one of the six current Creative Commons licences or CC0, with
what it lets others do and a link to its terms; the licence field
suggests well-known licences and takes any other as typed, kept in one
spelling (`cc by-nc-sa 3.0 us` is `CC-BY-NC-SA-3.0-US`); the year sits at
its end (1000 to next year), the holder below. An older or ported
Creative Commons licence gets a note naming its current version. A new
sub-studyset starts with the licence of the one it is in.

Left out until publishing exists: the warnings about sub-studysets'
licences that a parent's doesn't cover. The licence shows for every
studyset, as there is no visibility yet.

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>
This commit is contained in:
Tizian 「ティツィアーン」 2026-10-08 23:48:43 +02:00
commit bd6016ee1b
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64
2 changed files with 1385 additions and 9 deletions

View file

@ -0,0 +1,26 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:halkyon_learn_flutter/src/feature/studyset/model/license.dart';
void main() {
test('a licence is kept in one spelling: known ones as known, Creative '
'Commons by its identifier, anything else as typed', () {
expect(canonicalLicense('Cc By 4.0'), 'CC-BY-4.0');
expect(canonicalLicense('cc_by_sa'), 'CC-BY-SA-4.0');
expect(canonicalLicense('public domain'), 'CC0-1.0');
expect(canonicalLicense('mit'), 'MIT');
expect(canonicalLicense('arr'), 'All Rights Reserved');
expect(canonicalLicense('cc by-nc-sa 3.0 us'), 'CC-BY-NC-SA-3.0-US');
expect(canonicalLicense(' My own terms '), 'My own terms');
expect(canonicalLicense(' '), isNull);
expect(canonicalLicense(null), isNull);
});
test('an older or ported Creative Commons licence has a current version; '
'a current one, or another licence, none', () {
expect(newerCreativeCommons('CC-BY-NC-SA-3.0-US'), 'CC-BY-NC-SA-4.0');
expect(newerCreativeCommons('CC-BY-2.0'), 'CC-BY-4.0');
expect(newerCreativeCommons('CC-BY-SA-4.0'), isNull);
expect(newerCreativeCommons('CC0-1.0'), isNull);
expect(newerCreativeCommons('MIT'), isNull);
});
}

View file

@ -644,6 +644,119 @@ void main() {
expect((content.frontLanguage, content.backLanguage), (null, null));
});
group('the licence', () {
/// Opens the studyset `japanese`'s sheet, scrolled to its licence. The
/// tests speak American English, as the device does.
Future<void> editLicense(WidgetTester tester) async {
await choose(tester, 'Edit');
await tester.ensureVisible(find.text('Help me choose'));
await tester.pumpAndSettle();
}
StudysetContent saved() => decodeStudysetPayload(gateway.writes.last.$2);
testWidgets('Help me choose asks three questions and fills in the '
'licence they come to', (tester) async {
stored('japanese', 'Japanese');
await showApp(tester, location: RoutePaths.studyset('japanese'));
await tester.pumpAndSettle();
await editLicense(tester);
await tester.tap(find.text('Help me choose'));
await tester.pumpAndSettle();
await tester.tap(find.text('Yes, share it under a license'));
await tester.pumpAndSettle();
await tester.tap(find.text('No'));
await tester.pumpAndSettle();
await tester.tap(find.text('Yes, if they share alike'));
await tester.pumpAndSettle();
expect(find.text('CC-BY-NC-SA-4.0'), findsOneWidget);
expect(
find.textContaining('the same license on adaptations'),
findsOneWidget,
);
await tester.tap(find.text('Use this license'));
await tester.pumpAndSettle();
await tester.tap(find.text('Save'));
await tester.pumpAndSettle();
expect(saved().license, 'CC-BY-NC-SA-4.0');
});
testWidgets('a typed licence is kept in its standard spelling, with its '
'year and holder; an older Creative Commons one gets a note', (
tester,
) async {
stored('japanese', 'Japanese');
await showApp(tester, location: RoutePaths.studyset('japanese'));
await tester.pumpAndSettle();
await editLicense(tester);
await tester.enterText(
find.widgetWithText(TextField, 'License'),
'cc by-nc-sa 3.0 us',
);
await tester.enterText(find.widgetWithText(TextField, 'Year'), '999');
await tester.pump();
expect(
find.text('Enter a year between 1000 and ${DateTime.now().year + 1}.'),
findsOneWidget,
);
expect(
tester
.widget<FilledButton>(find.widgetWithText(FilledButton, 'Save'))
.onPressed,
isNull,
);
await tester.enterText(find.widgetWithText(TextField, 'Year'), '2006');
await tester.enterText(
find.widgetWithText(TextField, 'License holder'),
'Tae Kim',
);
await tester.pump();
expect(
find.textContaining(
'CC-BY-NC-SA-3.0-US is an older Creative Commons license',
),
findsOneWidget,
);
await tester.tap(find.text('Save'));
await tester.pumpAndSettle();
final content = saved();
expect(
(content.license, content.licenseHolder, content.licenseYear),
('CC-BY-NC-SA-3.0-US', 'Tae Kim', 2006),
);
});
testWidgets('a new sub-studyset starts with the licence of the one it is '
'in', (tester) async {
stored(
'japanese',
'Japanese',
license: 'CC-BY-NC-SA-3.0-US',
licenseHolder: 'Tae Kim',
licenseYear: 2006,
);
await showApp(tester, location: RoutePaths.studyset('japanese'));
await tester.pumpAndSettle();
await tester.tap(find.text('New sub-studyset'));
await tester.pumpAndSettle();
await tester.enterText(find.widgetWithText(TextField, 'Title *'), 'Kana');
await tester.pump();
await tester.tap(find.text('Create'));
await tester.pumpAndSettle();
final content = saved();
expect(
(content.license, content.licenseHolder, content.licenseYear),
('CC-BY-NC-SA-3.0-US', 'Tae Kim', 2006),
);
});
});
testWidgets('with two notes or more, Reorder beside the tabs lets them be '
'dragged into another order, until Done', (tester) async {
stored('japanese', 'Japanese');