fix(app): Reorder among older items, export and text import that hold up
- Reorder: rows from before an order existed all share one position, and one moved among them landed at their end; the run it goes into now gets positions of its own (placeAmong), for notes, files, paths, steps and sub-studysets. Rows whose deletion can still be undone count for nothing, as the list leaves them out, and a dropped row takes its place at once instead of snapping back until the order is read again. - Export: two studysets inside each other (moved so on two devices offline) no longer make it run forever; links to files whose names hold #, % or brackets work; PDFs, images and sounds go into the zip without being compressed again; any error says the export failed. - Text import: a real CSV reader (quoted fields over several lines, a back over several quoted fields, a stray quote spoiling only its own line), the separator guessed from the text (semicolons from German spreadsheets), and files in UTF-16 or Windows-1252 read as meant. - A tab whose notes, paths or files couldn't be read says so rather than "No … yet"; an Anki import from the Library that fails leaves no empty studyset behind. 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:
parent
d5e5945d93
commit
084af278b5
2 changed files with 727 additions and 138 deletions
|
|
@ -1,3 +1,5 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:halkyon_learn_flutter/src/feature/card/model/card_text_import.dart';
|
||||
|
||||
|
|
@ -61,4 +63,59 @@ void main() {
|
|||
expect(CardSeparator.forFile('words.tsv'), CardSeparator.tab);
|
||||
expect(CardSeparator.forFile('words.txt'), CardSeparator.tab);
|
||||
});
|
||||
|
||||
test('a quoted field may run over several lines, and a back over '
|
||||
'several fields keeps none of their quotes', () {
|
||||
expect(
|
||||
parseCardLines(
|
||||
'"word","line one\nline two"\n"a","b","c"\nnext,one',
|
||||
CardSeparator.comma,
|
||||
),
|
||||
[
|
||||
(front: 'word', back: 'line one\nline two'),
|
||||
(front: 'a', back: 'b,c'),
|
||||
(front: 'next', back: 'one'),
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
test('a quote that never closes spoils only its own line', () {
|
||||
expect(
|
||||
parseCardLines('"hello,hola\nsí,yes', CardSeparator.comma),
|
||||
[(front: 'sí', back: 'yes')],
|
||||
);
|
||||
});
|
||||
|
||||
test('the separator is guessed from what the lines hold: semicolons as '
|
||||
'German spreadsheets write them, whatever the name', () {
|
||||
expect(
|
||||
guessSeparator(
|
||||
'Wort;Bedeutung, mit Komma\nHaus;house',
|
||||
fileName: 'w.csv',
|
||||
),
|
||||
CardSeparator.semicolon,
|
||||
);
|
||||
expect(
|
||||
guessSeparator('hola\thello, hi\nsí\tyes', fileName: 'w.csv'),
|
||||
CardSeparator.tab,
|
||||
);
|
||||
expect(
|
||||
guessSeparator('nothing here', fileName: 'w.csv'),
|
||||
CardSeparator.comma,
|
||||
);
|
||||
});
|
||||
|
||||
test('a file is read in whichever encoding it was saved: UTF-8, UTF-16 '
|
||||
'with its mark, or Windows-1252', () {
|
||||
expect(decodeCardText(utf8.encode('Straße;street')), 'Straße;street');
|
||||
expect(
|
||||
decodeCardText([0xFF, 0xFE, 0x68, 0, 0x3B, 0, 0x42, 0x30]),
|
||||
'h;あ',
|
||||
);
|
||||
// "Café – €" as Windows-1252.
|
||||
expect(
|
||||
decodeCardText([0x43, 0x61, 0x66, 0xE9, 0x20, 0x96, 0x20, 0x80]),
|
||||
'Café – €',
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -128,14 +128,46 @@ void main() {
|
|||
expect(await stored(), [('hello, friend', 'hola amigo')]);
|
||||
});
|
||||
|
||||
testWidgets("a file that isn't UTF-8 text says so", (tester) async {
|
||||
testWidgets('a file that holds no text says so', (tester) async {
|
||||
await showApp(tester);
|
||||
await openImport(tester);
|
||||
files.next = (name: 'words.csv', bytes: Uint8List.fromList([0xff, 0xfe]));
|
||||
files.next = (
|
||||
name: 'words.csv',
|
||||
bytes: Uint8List.fromList([0x89, 0x50, 0x4E, 0x47, 0x00, 0x00, 0x1A]),
|
||||
);
|
||||
|
||||
await tester.tap(find.text('Load a CSV or TSV file'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text("This file isn't text in UTF-8."), findsOneWidget);
|
||||
expect(find.text("This file doesn't hold text."), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('a file an older spreadsheet saved (Windows-1252, semicolons) '
|
||||
'is read as it is meant', (tester) async {
|
||||
await showApp(tester);
|
||||
await openImport(tester);
|
||||
// "Café;coffee" as Windows-1252.
|
||||
files.next = (
|
||||
name: 'words.csv',
|
||||
bytes: Uint8List.fromList([
|
||||
0x43,
|
||||
0x61,
|
||||
0x66,
|
||||
0xE9,
|
||||
0x3B,
|
||||
0x63,
|
||||
0x6F,
|
||||
0x66,
|
||||
0x66,
|
||||
0x65,
|
||||
0x65,
|
||||
]),
|
||||
);
|
||||
|
||||
await tester.tap(find.text('Load a CSV or TSV file'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.widgetWithText(TextField, 'Café;coffee'), findsOneWidget);
|
||||
expect(find.text('1 card ready to import.'), findsOneWidget);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue