- Introduced a new Flutter project for the cxyz Android manga/comic reader, including essential files such as pubspec.yaml, AndroidManifest.xml, and Gradle configuration. - Added .gitignore files to exclude unnecessary files and directories for both the main project and Android-specific files. - Implemented initial README.md with project description, features, setup instructions, and API details for ComicLand, MangaRead, and MangaTown. - Configured analysis options and dependencies in pubspec.yaml to support Flutter development. - Established a pnpm workspace and lockfile for dependency management. - Included various Android resources such as icons, styles, and launch backgrounds for a complete application setup.
167 lines
5.0 KiB
Dart
167 lines
5.0 KiB
Dart
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:cxyz_android/sources/source.dart';
|
|
import 'package:cxyz_android/sources/source_flags.dart';
|
|
import 'package:cxyz_android/sources/comicland/comicland_source.dart';
|
|
import 'package:cxyz_android/sources/mangaread/mangaread_source.dart';
|
|
import 'package:cxyz_android/sources/mangaread/urls.dart';
|
|
import 'package:cxyz_android/sources/mangatown/mangatown_source.dart';
|
|
import 'package:cxyz_android/sources/registry.dart';
|
|
|
|
void main() {
|
|
group('parseEnabledSourceIds', () {
|
|
test('default comicland token', () {
|
|
expect(parseEnabledSourceIds('comicland'), {SourceId.comicland});
|
|
});
|
|
|
|
test('comma-separated both', () {
|
|
expect(
|
|
parseEnabledSourceIds('comicland,mangaread'),
|
|
{SourceId.comicland, SourceId.mangaread},
|
|
);
|
|
});
|
|
|
|
test('mangaread only', () {
|
|
expect(parseEnabledSourceIds('mangaread'), {SourceId.mangaread});
|
|
});
|
|
|
|
test('all enables every SourceId', () {
|
|
expect(parseEnabledSourceIds('all'), SourceId.values.toSet());
|
|
expect(parseEnabledSourceIds('ALL'), SourceId.values.toSet());
|
|
expect(parseEnabledSourceIds('all,mangaread'), SourceId.values.toSet());
|
|
});
|
|
|
|
test('mangatown token', () {
|
|
expect(parseEnabledSourceIds('mangatown'), {SourceId.mangatown});
|
|
});
|
|
|
|
test('ignores unknown and falls back when empty', () {
|
|
expect(parseEnabledSourceIds(''), {SourceId.comicland});
|
|
expect(parseEnabledSourceIds('nope,also-bad'), {SourceId.comicland});
|
|
});
|
|
|
|
test('trims and is case-insensitive', () {
|
|
expect(
|
|
parseEnabledSourceIds(' MangaRead , ComicLand '),
|
|
{SourceId.mangaread, SourceId.comicland},
|
|
);
|
|
});
|
|
});
|
|
|
|
group('SourceRegistry gating', () {
|
|
test('registers only enabled ids', () {
|
|
final reg = SourceRegistry(
|
|
enabled: {SourceId.mangaread},
|
|
mangaRead: MangaReadSource(),
|
|
);
|
|
expect(reg.all.map((s) => s.id), [SourceId.mangaread]);
|
|
expect(reg.hasMultiple, isFalse);
|
|
});
|
|
|
|
test('dual sources preserve declaration order', () {
|
|
final reg = SourceRegistry(
|
|
enabled: {SourceId.mangaread, SourceId.comicland},
|
|
comicLand: ComicLandSource(),
|
|
mangaRead: MangaReadSource(),
|
|
);
|
|
expect(reg.all.map((s) => s.id).toList(), [
|
|
SourceId.comicland,
|
|
SourceId.mangaread,
|
|
]);
|
|
expect(reg.hasMultiple, isTrue);
|
|
});
|
|
|
|
test('all three sources preserve declaration order', () {
|
|
final reg = SourceRegistry(
|
|
enabled: {
|
|
SourceId.mangatown,
|
|
SourceId.mangaread,
|
|
SourceId.comicland,
|
|
},
|
|
comicLand: ComicLandSource(),
|
|
mangaRead: MangaReadSource(),
|
|
mangaTown: MangaTownSource(),
|
|
);
|
|
expect(reg.all.map((s) => s.id).toList(), [
|
|
SourceId.comicland,
|
|
SourceId.mangaread,
|
|
SourceId.mangatown,
|
|
]);
|
|
});
|
|
});
|
|
|
|
group('catalogMenus capabilities', () {
|
|
test('ComicLand exposes newest, popular, official, uncensored, recommended, search', () {
|
|
final kinds =
|
|
ComicLandSource().catalogMenus.map((m) => m.kind).toList();
|
|
expect(kinds, [
|
|
CatalogMenuKind.latest,
|
|
CatalogMenuKind.popular,
|
|
CatalogMenuKind.official,
|
|
CatalogMenuKind.uncensored,
|
|
CatalogMenuKind.recommended,
|
|
CatalogMenuKind.search,
|
|
]);
|
|
expect(
|
|
ComicLandSource().catalogMenus.first.title,
|
|
'Newest',
|
|
);
|
|
});
|
|
|
|
test('MangaRead exposes latest and search only', () {
|
|
final kinds =
|
|
MangaReadSource().catalogMenus.map((m) => m.kind).toList();
|
|
expect(kinds, [
|
|
CatalogMenuKind.latest,
|
|
CatalogMenuKind.search,
|
|
]);
|
|
expect(kinds.contains(CatalogMenuKind.popular), isFalse);
|
|
});
|
|
|
|
test('MangaTown exposes latest, hot variants, directory, search', () {
|
|
final kinds =
|
|
MangaTownSource().catalogMenus.map((m) => m.kind).toList();
|
|
expect(kinds, [
|
|
CatalogMenuKind.latest,
|
|
CatalogMenuKind.popular,
|
|
CatalogMenuKind.hotToday,
|
|
CatalogMenuKind.hotWeek,
|
|
CatalogMenuKind.hotMonth,
|
|
CatalogMenuKind.directory,
|
|
CatalogMenuKind.search,
|
|
]);
|
|
});
|
|
});
|
|
|
|
group('mangaread urls', () {
|
|
test('mangaSlugFromUrl', () {
|
|
expect(
|
|
mangaSlugFromUrl(
|
|
'https://www.mangaread.org/manga/the-chaebeols-youngest-son/chapter-560/',
|
|
),
|
|
'the-chaebeols-youngest-son',
|
|
);
|
|
});
|
|
|
|
test('mangaBaseUrlFromListingUrl', () {
|
|
expect(
|
|
mangaBaseUrlFromListingUrl(
|
|
'https://www.mangaread.org/manga/foo-bar/chapter-1/',
|
|
),
|
|
'https://www.mangaread.org/manga/foo-bar/',
|
|
);
|
|
});
|
|
|
|
test('listingUrlForSlug', () {
|
|
expect(
|
|
listingUrlForSlug('foo-bar'),
|
|
'https://www.mangaread.org/manga/foo-bar/',
|
|
);
|
|
});
|
|
|
|
test('chapterNumberFromTitle', () {
|
|
expect(chapterNumberFromTitle('Chapter 560'), '560');
|
|
expect(chapterNumberFromTitle('Ch. 12.5'), '12.5');
|
|
});
|
|
});
|
|
}
|