Files
sharedinbox/test/widget/email_thread_list_controller_test.dart
T
Claude CodeandClaude Opus 4.7 01304572cb test(email-list): drop redundant accountId arg to satisfy --fatal-infos
The CI dart analyze step runs with --fatal-infos so any
avoid_redundant_argument_values info fails the build. The new
EmailThreadListController test passed the default accountId explicitly,
tripping the lint. Use the default instead.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-10 13:45:10 +00:00

108 lines
3.3 KiB
Dart

import 'package:flutter_test/flutter_test.dart';
import 'package:sharedinbox/core/models/email.dart';
import 'package:sharedinbox/ui/widgets/email_thread_list.dart';
EmailThread _t(String id, {String accountId = 'acc-1'}) => EmailThread(
threadId: id,
subject: id,
participants: const [],
latestDate: DateTime(2024, 6),
messageCount: 1,
hasUnread: false,
isFlagged: false,
latestEmailId: id,
emailIds: [id],
accountId: accountId,
mailboxPath: 'INBOX',
);
void main() {
group('EmailThreadListController', () {
test('toggle adds then removes a thread id and fires notifications', () {
final ctrl = EmailThreadListController()
..updateThreads([_t('a'), _t('b')]);
var notifications = 0;
ctrl.addListener(() => notifications++);
expect(ctrl.isSelecting, isFalse);
ctrl.toggle(_t('a'));
expect(ctrl.isSelecting, isTrue);
expect(ctrl.selectionCount, 1);
expect(ctrl.isSelected(_t('a')), isTrue);
expect(notifications, 1);
ctrl.toggle(_t('a'));
expect(ctrl.isSelecting, isFalse);
expect(ctrl.selectionCount, 0);
expect(notifications, 2);
});
test('selectAll selects every visible thread', () {
final ctrl = EmailThreadListController()
..updateThreads([_t('a'), _t('b'), _t('c')]);
ctrl.selectAll();
expect(ctrl.selectionCount, 3);
expect(ctrl.selectedIds, {'a', 'b', 'c'});
});
test('clear empties the selection and notifies once', () {
final ctrl = EmailThreadListController()
..updateThreads([_t('a'), _t('b')])
..toggle(_t('a'))
..toggle(_t('b'));
var notifications = 0;
ctrl.addListener(() => notifications++);
ctrl.clear();
expect(ctrl.isSelecting, isFalse);
expect(notifications, 1);
// Clearing an already-empty selection does not notify again.
ctrl.clear();
expect(notifications, 1);
});
test('updateThreads drops selections that are no longer visible', () {
final ctrl = EmailThreadListController()
..updateThreads([_t('a'), _t('b'), _t('c')])
..toggle(_t('a'))
..toggle(_t('c'));
expect(ctrl.selectionCount, 2);
ctrl.updateThreads([_t('a'), _t('b')]);
// 'c' is no longer visible, so it gets dropped.
expect(ctrl.selectionCount, 1);
expect(ctrl.selectedIds, {'a'});
});
test('selectedThreads preserves the visible-list order', () {
final a = _t('a');
final b = _t('b');
final c = _t('c');
final ctrl = EmailThreadListController()
..updateThreads([a, b, c])
..toggle(c)
..toggle(a);
// Selection order is insertion (c, a), but selectedThreads must follow
// the visible-list order (a, c).
expect(ctrl.selectedThreads.map((t) => t.threadId), ['a', 'c']);
});
test('multi-account threads are kept independent in the selection', () {
final ctrl = EmailThreadListController()
..updateThreads([
_t('a'),
_t('b', accountId: 'acc-2'),
]);
ctrl.selectAll();
final byAccount = <String, int>{};
for (final t in ctrl.selectedThreads) {
byAccount[t.accountId] = (byAccount[t.accountId] ?? 0) + 1;
}
expect(byAccount, {'acc-1': 1, 'acc-2': 1});
});
});
}