2026-10-11 09:33:25 +03:00
|
|
|
import 'package:drift/drift.dart' show Value;
|
|
|
|
|
import 'package:drift/native.dart';
|
|
|
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
|
|
2026-10-11 13:12:48 +03:00
|
|
|
import 'package:mews/application/controllers/bookmarks_controller.dart';
|
2026-10-11 09:33:25 +03:00
|
|
|
import 'package:mews/application/controllers/reader_settings_controller.dart';
|
2026-10-11 13:12:48 +03:00
|
|
|
import 'package:mews/application/controllers/subscriptions_controller.dart';
|
2026-10-11 09:33:25 +03:00
|
|
|
import 'package:mews/database/database.dart';
|
|
|
|
|
import 'package:mews/mews/document.dart';
|
|
|
|
|
|
|
|
|
|
void main() {
|
|
|
|
|
late MewsDatabase db;
|
|
|
|
|
|
|
|
|
|
setUp(() {
|
|
|
|
|
db = MewsDatabase.test(NativeDatabase.memory());
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
tearDown(() async {
|
|
|
|
|
await db.close();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('settings round-trip through the database', () async {
|
|
|
|
|
final settings = ReaderSettingsController(db);
|
|
|
|
|
await settings.setFont('Atkinson Hyperlegible');
|
|
|
|
|
await settings.increaseSize();
|
|
|
|
|
await settings.setThemeMode('dark');
|
|
|
|
|
await settings.setVariant('warm');
|
|
|
|
|
|
|
|
|
|
final reloaded = ReaderSettingsController(db);
|
|
|
|
|
await reloaded.load();
|
|
|
|
|
expect(reloaded.font.value, 'Atkinson Hyperlegible');
|
|
|
|
|
expect(reloaded.fontScale.value, 1.125);
|
|
|
|
|
expect(reloaded.themeMode.value, 'dark');
|
|
|
|
|
expect(reloaded.variant.value, 'warm');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('text size stays within its limits', () async {
|
|
|
|
|
final settings = ReaderSettingsController(db);
|
|
|
|
|
for (var i = 0; i < 30; i++) {
|
|
|
|
|
await settings.increaseSize();
|
|
|
|
|
}
|
|
|
|
|
expect(settings.fontScale.value, ReaderSettingsController.maxScale);
|
|
|
|
|
for (var i = 0; i < 40; i++) {
|
|
|
|
|
await settings.decreaseSize();
|
|
|
|
|
}
|
|
|
|
|
expect(settings.fontScale.value, ReaderSettingsController.minScale);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('bookmarks toggle and persist', () async {
|
2026-10-11 13:12:48 +03:00
|
|
|
final bookmarks = BookmarksController(db);
|
|
|
|
|
await bookmarks.load();
|
|
|
|
|
await bookmarks.toggle('https://example.com/', 'Example');
|
|
|
|
|
expect(bookmarks.isBookmarked('https://example.com/'), isTrue);
|
2026-10-11 09:33:25 +03:00
|
|
|
|
2026-10-11 13:12:48 +03:00
|
|
|
final reloaded = BookmarksController(db);
|
2026-10-11 09:33:25 +03:00
|
|
|
await reloaded.load();
|
|
|
|
|
expect(reloaded.isBookmarked('https://example.com/'), isTrue);
|
|
|
|
|
|
2026-10-11 13:12:48 +03:00
|
|
|
await reloaded.toggle('https://example.com/', 'Example');
|
2026-10-11 09:33:25 +03:00
|
|
|
expect(reloaded.isBookmarked('https://example.com/'), isFalse);
|
|
|
|
|
});
|
|
|
|
|
|
2026-10-11 13:12:48 +03:00
|
|
|
test('subscribing takes on the back catalogue without flagging it', () async {
|
|
|
|
|
final subscriptions = SubscriptionsController(db);
|
|
|
|
|
await subscriptions.load();
|
2026-10-11 09:33:25 +03:00
|
|
|
|
|
|
|
|
final firstPost = MewsEntry(
|
2026-10-11 13:12:48 +03:00
|
|
|
url: 'https://example.com/a.html',
|
|
|
|
|
date: DateTime(2026, 9, 21),
|
|
|
|
|
title: 'First post');
|
|
|
|
|
await subscriptions.toggle(_page('https://example.com/', [firstPost]));
|
|
|
|
|
expect(subscriptions.isSubscribed('https://example.com/'), isTrue);
|
2026-10-11 09:33:25 +03:00
|
|
|
|
2026-10-11 13:12:48 +03:00
|
|
|
// The post was stored, but read, so nothing is waiting for the reader.
|
|
|
|
|
expect(await db.unreadCount(), 0);
|
2026-10-11 09:33:25 +03:00
|
|
|
|
2026-10-11 13:12:48 +03:00
|
|
|
// Opening the page again finds nothing new.
|
|
|
|
|
expect(await subscriptions.refreshSite(_page('https://example.com/', [firstPost])),
|
|
|
|
|
isEmpty);
|
|
|
|
|
|
|
|
|
|
// A later post is new, and stays unread until the reader opens it.
|
2026-10-11 09:33:25 +03:00
|
|
|
final newPost = MewsEntry(
|
2026-10-11 13:12:48 +03:00
|
|
|
url: 'https://example.com/b.html',
|
|
|
|
|
date: DateTime(2026, 10, 8),
|
|
|
|
|
title: 'Second post');
|
|
|
|
|
final fresh = await subscriptions
|
|
|
|
|
.refreshSite(_page('https://example.com/', [newPost, firstPost]));
|
|
|
|
|
expect(fresh.map((e) => e.url), ['https://example.com/b.html']);
|
|
|
|
|
expect(await db.unreadCount(siteUrl: 'https://example.com/'), 1);
|
|
|
|
|
|
|
|
|
|
// Finding it a second time does not report it again.
|
|
|
|
|
expect(
|
|
|
|
|
await subscriptions
|
|
|
|
|
.refreshSite(_page('https://example.com/', [newPost, firstPost])),
|
|
|
|
|
isEmpty);
|
|
|
|
|
expect(await db.unreadCount(), 1);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('unread posts survive a restart', () async {
|
|
|
|
|
final subscriptions = SubscriptionsController(db);
|
|
|
|
|
await subscriptions.load();
|
|
|
|
|
await subscriptions.toggle(_page('https://example.com/', const []));
|
|
|
|
|
await subscriptions.refreshSite(_page('https://example.com/', [
|
|
|
|
|
MewsEntry(
|
|
|
|
|
url: 'https://example.com/a.html',
|
|
|
|
|
date: DateTime(2026, 9, 21),
|
|
|
|
|
title: 'First post'),
|
|
|
|
|
]));
|
|
|
|
|
|
|
|
|
|
// A fresh controller over the same database sees the same unread post,
|
|
|
|
|
// which the old in-memory count could not do.
|
|
|
|
|
final restarted = SubscriptionsController(db);
|
|
|
|
|
await restarted.load();
|
|
|
|
|
expect(restarted.isSubscribed('https://example.com/'), isTrue);
|
|
|
|
|
expect(await db.unreadCount(), 1);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('leaving a site takes its posts with it', () async {
|
|
|
|
|
final subscriptions = SubscriptionsController(db);
|
|
|
|
|
await subscriptions.load();
|
|
|
|
|
await subscriptions.toggle(_page('https://example.com/', const []));
|
|
|
|
|
await subscriptions.refreshSite(_page('https://example.com/', [
|
|
|
|
|
MewsEntry(
|
|
|
|
|
url: 'https://example.com/a.html',
|
|
|
|
|
date: DateTime(2026, 9, 21),
|
|
|
|
|
title: 'First post'),
|
|
|
|
|
]));
|
|
|
|
|
expect(await db.unreadCount(), 1);
|
|
|
|
|
|
|
|
|
|
await subscriptions.remove('https://example.com/');
|
|
|
|
|
expect(subscriptions.isSubscribed('https://example.com/'), isFalse);
|
|
|
|
|
expect(await db.unreadCount(), 0);
|
2026-10-11 09:33:25 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('caches pages for offline reopening', () async {
|
|
|
|
|
await db.saveCachedPage(CachedPagesCompanion.insert(
|
|
|
|
|
url: 'https://example.com/page.html',
|
|
|
|
|
source: 'html',
|
|
|
|
|
title: const Value('Page'),
|
|
|
|
|
etag: const Value('"v1"'),
|
|
|
|
|
));
|
|
|
|
|
final cached = await db.cachedPage('https://example.com/page.html');
|
|
|
|
|
expect(cached!.source, 'html');
|
|
|
|
|
expect(cached.etag, '"v1"');
|
|
|
|
|
expect(cached.title, 'Page');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('records history and returns the last visited page', () async {
|
|
|
|
|
await db.recordVisit('https://a.example/', 'A');
|
|
|
|
|
await db.recordVisit('https://b.example/', 'B');
|
|
|
|
|
expect(await db.lastVisitedUrl(), 'https://b.example/');
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MewsPage _page(String url, List<MewsEntry> entries) => MewsPage(
|
|
|
|
|
url: url,
|
|
|
|
|
body: const [],
|
|
|
|
|
lang: 'en',
|
|
|
|
|
direction: TextDirection.ltr,
|
|
|
|
|
variant: MewsVariant.plain,
|
|
|
|
|
entries: entries,
|
|
|
|
|
specVersion: '0.1',
|
|
|
|
|
);
|