feat(app): import flashcards from text

Import content gains Flashcards: pasted, or from a CSV or TSV file, one
card per line, split at its first tab, comma or semicolon (a .csv picks
commas), lines without both sides left out. Quoted fields are read as
spreadsheets write them, and a UTF-8 byte order mark is ignored. The
cards come in at the studyset's end with the origin "text", so the
dashboard doesn't count them as written.

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 14:43:49 +02:00
commit 18c1370bf6
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -0,0 +1,64 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:halkyon_learn_flutter/src/feature/card/model/card_text_import.dart';
void main() {
test('one card per line, split at its first separator, so a back may '
'hold one too', () {
expect(parseCardLines('hola\thello\nadiós\tgood\tbye', CardSeparator.tab), [
(front: 'hola', back: 'hello'),
(front: 'adiós', back: 'good\tbye'),
]);
});
test('leaves out blank lines, lines without a separator and lines with '
'an empty side, such as a header without a back', () {
expect(
parseCardLines(
'Spanish vocab\n\nhola;hello\n;nothing in front\nnothing behind;\n',
CardSeparator.semicolon,
),
[(front: 'hola', back: 'hello')],
);
});
test('trims both sides, and reads Windows line endings', () {
expect(
parseCardLines(' hola , hello \r\nsí,yes\r\n', CardSeparator.comma),
[
(front: 'hola', back: 'hello'),
(front: 'sí', back: 'yes'),
],
);
});
test('reads quoted fields as spreadsheets write them: separators inside, '
'a doubled quote for one', () {
expect(
parseCardLines(
'"hello, ""friend""",hola amigo\nthank you,"gracias, señor"',
CardSeparator.comma,
),
[
(front: 'hello, "friend"', back: 'hola amigo'),
(front: 'thank you', back: 'gracias, señor'),
],
);
});
test('leaves out a line whose quoted front never closes', () {
expect(parseCardLines('"hello,hola', CardSeparator.comma), isEmpty);
});
test('ignores the byte order mark spreadsheets put before UTF-8 text', () {
expect(parseCardLines('hola,hello', CardSeparator.comma), [
(front: 'hola', back: 'hello'),
]);
});
test('a file says its separator by its name: commas for .csv, tabs for '
'the rest', () {
expect(CardSeparator.forFile('words.CSV'), CardSeparator.comma);
expect(CardSeparator.forFile('words.tsv'), CardSeparator.tab);
expect(CardSeparator.forFile('words.txt'), CardSeparator.tab);
});
}