feat(todo): comments on a task (ADR 0033, D36)
Plain text with links, written, edited and deleted in the task's sheet, counted on its row. They are sealed in `task_comment`, the task they belong to with them, and go with their task. Whats-New: Comments: write on a task, edit or delete what you wrote, and open its links. Whats-New-De: Kommentare: Schreibe zu einer Aufgabe, bearbeite oder lösche es und öffne Links darin. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
6385d84cff
commit
016b74ff74
1 changed files with 20953 additions and 6 deletions
|
|
@ -0,0 +1,80 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:halkyon_todo_flutter/src/core/sealed/json_payload.dart';
|
||||
import 'package:halkyon_todo_flutter/src/feature/comment/data/comment_payload.dart';
|
||||
import 'package:halkyon_todo_flutter/src/feature/comment/model/comment.dart';
|
||||
|
||||
void main() {
|
||||
final written = DateTime.utc(2026, 11, 6, 19, 16);
|
||||
|
||||
group("a comment's payload", () {
|
||||
test('comes back as it went, edited or not', () {
|
||||
final plain = CommentContent(
|
||||
taskId: 't1',
|
||||
text: 'Ask the vet',
|
||||
writtenAt: written,
|
||||
);
|
||||
final edited = CommentContent(
|
||||
taskId: 't1',
|
||||
text: 'Ask the vet again',
|
||||
writtenAt: written,
|
||||
editedAt: written.add(const Duration(hours: 1)),
|
||||
);
|
||||
|
||||
expect(decodeComment(encodeComment(plain)), plain);
|
||||
expect(decodeComment(encodeComment(edited)), edited);
|
||||
});
|
||||
|
||||
test('of a later schema is not read', () {
|
||||
expect(
|
||||
decodeComment(encodeJson(commentSchemaVersion + 1, {'taskId': 't'})),
|
||||
isNull,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
group('links in a comment', () {
|
||||
List<String?> urls(String text) => [
|
||||
for (final piece in linkPieces(text)) piece.url?.toString(),
|
||||
];
|
||||
|
||||
test('plain text stays one piece', () {
|
||||
expect(linkPieces('Nothing here'), [
|
||||
(text: 'Nothing here', url: null),
|
||||
]);
|
||||
});
|
||||
|
||||
test('a link is found, without the sentence ending after it', () {
|
||||
expect(linkPieces('See https://example.org/a. Then go'), [
|
||||
(text: 'See ', url: null),
|
||||
(
|
||||
text: 'https://example.org/a',
|
||||
url: Uri.parse('https://example.org/a'),
|
||||
),
|
||||
(text: '. Then go', url: null),
|
||||
]);
|
||||
});
|
||||
|
||||
test('nothing is lost between the pieces', () {
|
||||
const text = 'a http://one.example b https://two.example/x?y=1 c';
|
||||
|
||||
expect(linkPieces(text).map((piece) => piece.text).join(), text);
|
||||
expect(urls(text).nonNulls, [
|
||||
'http://one.example',
|
||||
'https://two.example/x?y=1',
|
||||
]);
|
||||
});
|
||||
|
||||
test('nothing but http and https ever links', () {
|
||||
for (final text in [
|
||||
'javascript:alert(1)',
|
||||
'file:///etc/passwd',
|
||||
'intent://scan#Intent;end',
|
||||
'tel:+491234',
|
||||
'mailto:someone@example.org',
|
||||
'https://',
|
||||
]) {
|
||||
expect(urls(text).nonNulls, isEmpty, reason: text);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
Loading…
Reference in a new issue