правки по лаунчеру
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:moonwell_launcher/app/design_system/mw_authentication.dart';
|
||||
import 'package:moonwell_launcher/app/theme/moonwell_design_system.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('authentication placeholders use muted text color', (
|
||||
tester,
|
||||
) async {
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: MoonWellTheme.create(MoonWellThemeVariant.forest),
|
||||
home: Scaffold(
|
||||
body: MwAuthenticationForm(
|
||||
usernameController: TextEditingController(),
|
||||
passwordController: TextEditingController(),
|
||||
onSubmit: () {},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final fieldContext = tester.element(find.byType(TextField).first);
|
||||
final hintColor = Theme.of(
|
||||
fieldContext,
|
||||
).inputDecorationTheme.hintStyle?.color;
|
||||
|
||||
expect(hintColor, const Color(0x69ECE4D0));
|
||||
});
|
||||
|
||||
testWidgets('registration mode exposes all API registration fields', (
|
||||
tester,
|
||||
) async {
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: MoonWellTheme.create(MoonWellThemeVariant.forest),
|
||||
home: Scaffold(
|
||||
body: SingleChildScrollView(
|
||||
child: MwAuthenticationForm(
|
||||
usernameController: TextEditingController(),
|
||||
emailController: TextEditingController(),
|
||||
passwordController: TextEditingController(),
|
||||
passwordConfirmationController: TextEditingController(),
|
||||
inviteCodeController: TextEditingController(),
|
||||
mode: MwAuthenticationMode.registration,
|
||||
termsAccepted: true,
|
||||
onSubmit: () {},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.text('СОЗДАТЬ АККАУНТ'), findsOneWidget);
|
||||
expect(find.text('EMAIL'), findsOneWidget);
|
||||
expect(find.text('ПОВТОРИТЕ ПАРОЛЬ'), findsOneWidget);
|
||||
expect(find.text('ИНВАЙТ-КОД (НЕОБЯЗАТЕЛЬНО)'), findsOneWidget);
|
||||
expect(
|
||||
find.text('Я принимаю правила сервера и пользовательское соглашение'),
|
||||
findsOneWidget,
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('authentication tabs request mode changes', (tester) async {
|
||||
MwAuthenticationMode? requestedMode;
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: MoonWellTheme.create(MoonWellThemeVariant.forest),
|
||||
home: Scaffold(
|
||||
body: MwAuthenticationForm(
|
||||
usernameController: TextEditingController(),
|
||||
passwordController: TextEditingController(),
|
||||
onModeChanged: (mode) => requestedMode = mode,
|
||||
onSubmit: () {},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.tap(find.text('РЕГИСТРАЦИЯ'));
|
||||
|
||||
expect(requestedMode, MwAuthenticationMode.registration);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:moonwell_launcher/app/design_system/mw_launcher_components.dart';
|
||||
import 'package:moonwell_launcher/app/theme/moonwell_design_system.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('game rail shows WoW and MoonWell Karts', (tester) async {
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: MoonWellTheme.create(MoonWellThemeVariant.forest),
|
||||
home: const Scaffold(
|
||||
body: SizedBox(width: 240, child: MwGameRail(username: 'Sindoring')),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.text('MOONWELL · WOW'), findsOneWidget);
|
||||
expect(find.text('OPEN BETA'), findsOneWidget);
|
||||
expect(find.text('S'), findsOneWidget);
|
||||
expect(find.text('MOONWELL KARTS'), findsOneWidget);
|
||||
expect(find.text('В РАЗРАБОТКЕ'), findsOneWidget);
|
||||
expect(find.text('SOON'), findsNothing);
|
||||
expect(find.textContaining('TIDES OF ELUNE'), findsNothing);
|
||||
expect(find.textContaining('PROJECT VERGE'), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('game rail settings button invokes settings callback', (
|
||||
tester,
|
||||
) async {
|
||||
var settingsRequested = false;
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: MoonWellTheme.create(MoonWellThemeVariant.forest),
|
||||
home: Scaffold(
|
||||
body: SizedBox(
|
||||
width: 240,
|
||||
child: MwGameRail(onSettings: () => settingsRequested = true),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.tap(find.byTooltip('Настройки'));
|
||||
|
||||
expect(settingsRequested, isTrue);
|
||||
});
|
||||
|
||||
testWidgets('sync area displays realm data received from API', (
|
||||
tester,
|
||||
) async {
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: MoonWellTheme.create(MoonWellThemeVariant.forest),
|
||||
home: const Scaffold(
|
||||
body: SizedBox(
|
||||
width: 1000,
|
||||
height: 150,
|
||||
child: MwSyncActionArea(
|
||||
state: MwSyncPresentation.ready,
|
||||
status: 'Клиент готов.',
|
||||
realmName: 'MoonWell',
|
||||
realmOnline: true,
|
||||
realmGameBuild: 12340,
|
||||
onPrimary: _noop,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.text('MOONWELL'), findsOneWidget);
|
||||
expect(find.text('ONLINE · BUILD 12340'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('news item invokes its link callback when pressed', (
|
||||
tester,
|
||||
) async {
|
||||
var pressed = false;
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: MoonWellTheme.create(MoonWellThemeVariant.forest),
|
||||
home: Scaffold(
|
||||
body: MwNewsItem(
|
||||
item: MwNewsItemData(
|
||||
title: 'Update 1.2',
|
||||
body: 'News body',
|
||||
onPressed: () => pressed = true,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.tap(find.text('Update 1.2'));
|
||||
|
||||
expect(pressed, isTrue);
|
||||
});
|
||||
|
||||
testWidgets('account affordance displays username without balance', (
|
||||
tester,
|
||||
) async {
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: MoonWellTheme.create(MoonWellThemeVariant.forest),
|
||||
home: Scaffold(
|
||||
body: MwAccountAffordance(username: 'PLAYERONE', onLogout: _noop),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.text('PLAYERONE'), findsOneWidget);
|
||||
expect(find.textContaining('1250'), findsNothing);
|
||||
expect(find.text('P'), findsOneWidget);
|
||||
|
||||
await tester.tap(find.byType(PopupMenuButton<String>));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Выйти'), findsOneWidget);
|
||||
expect(find.text('Настройки'), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('settings panel excludes logout action', (tester) async {
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: MoonWellTheme.create(MoonWellThemeVariant.forest),
|
||||
home: Scaffold(
|
||||
body: MwSettingsPanel(
|
||||
installationPath: r'C:\Games\MoonWell',
|
||||
onChooseDirectory: _noop,
|
||||
onVerify: _noop,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.text('Выбрать папку'), findsOneWidget);
|
||||
expect(find.text('Проверить файлы'), findsOneWidget);
|
||||
expect(find.text('Выйти'), findsNothing);
|
||||
});
|
||||
}
|
||||
|
||||
void _noop() {}
|
||||
@@ -0,0 +1,75 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:moonwell_launcher/app/design_system/mw_shells.dart';
|
||||
import 'package:moonwell_launcher/app/theme/moonwell_design_system.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('brand mark fits its requested width without overflow', (
|
||||
tester,
|
||||
) async {
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: MoonWellTheme.create(MoonWellThemeVariant.forest),
|
||||
home: const Scaffold(body: Center(child: MwBrandMark(width: 220))),
|
||||
),
|
||||
);
|
||||
|
||||
expect(tester.takeException(), isNull);
|
||||
});
|
||||
|
||||
testWidgets('login shell renders the key art asset', (tester) async {
|
||||
await tester.binding.setSurfaceSize(const Size(1280, 720));
|
||||
addTearDown(() => tester.binding.setSurfaceSize(null));
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: MoonWellTheme.create(MoonWellThemeVariant.forest),
|
||||
home: MwLoginShell(
|
||||
form: const SizedBox(),
|
||||
onDrag: _noop,
|
||||
onDoubleTap: _noop,
|
||||
onMinimize: _noop,
|
||||
onMaximizeRestore: _noop,
|
||||
onClose: _noop,
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final image = tester.widget<Image>(find.byType(Image));
|
||||
expect((image.image as AssetImage).assetName, 'assets/login_key_art.png');
|
||||
expect(tester.takeException(), isNull);
|
||||
});
|
||||
|
||||
testWidgets('launcher shell renders the main key art asset', (tester) async {
|
||||
await tester.binding.setSurfaceSize(const Size(1280, 720));
|
||||
addTearDown(() => tester.binding.setSurfaceSize(null));
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: MoonWellTheme.create(MoonWellThemeVariant.forest),
|
||||
home: const MwLauncherShell(
|
||||
news: SizedBox(),
|
||||
syncActions: SizedBox(),
|
||||
statusBar: SizedBox(),
|
||||
account: SizedBox(),
|
||||
onDrag: _noop,
|
||||
onDoubleTap: _noop,
|
||||
onMinimize: _noop,
|
||||
onMaximizeRestore: _noop,
|
||||
onClose: _noop,
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final image = tester.widget<Image>(find.byType(Image));
|
||||
expect(
|
||||
(image.image as AssetImage).assetName,
|
||||
'assets/launcher_key_art.png',
|
||||
);
|
||||
expect(tester.takeException(), isNull);
|
||||
});
|
||||
}
|
||||
|
||||
void _noop() {}
|
||||
@@ -1,15 +1,18 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:moonwell_launcher/core/moonwell_theme_variant.dart';
|
||||
import 'package:moonwell_launcher/app/home_screen/bloc/home_screen_bloc.dart';
|
||||
import 'package:moonwell_launcher/core/moonwell_theme_variant.dart';
|
||||
import 'package:moonwell_launcher/features/downloader/domain/entities/download_progress.dart';
|
||||
import 'package:moonwell_launcher/features/launcher/application/client_sync_use_case.dart';
|
||||
import 'package:moonwell_launcher/features/launcher/data/game_installation_service.dart';
|
||||
import 'package:moonwell_launcher/features/launcher/data/launcher_api_client.dart';
|
||||
import 'package:moonwell_launcher/features/launcher/domain/entities/client_installation_snapshot.dart';
|
||||
import 'package:moonwell_launcher/features/launcher/domain/entities/client_manifest.dart';
|
||||
import 'package:moonwell_launcher/features/launcher/domain/entities/client_sync_status.dart';
|
||||
import 'package:moonwell_launcher/features/launcher/domain/entities/launcher_account.dart';
|
||||
import 'package:moonwell_launcher/features/launcher/domain/entities/launcher_news_item.dart';
|
||||
import 'package:moonwell_launcher/features/launcher/domain/entities/launcher_realm.dart';
|
||||
import 'package:moonwell_launcher/features/launcher/domain/entities/launcher_session.dart';
|
||||
import 'package:moonwell_launcher/features/preferences/domain/repositories/preferences_repository.dart';
|
||||
|
||||
@@ -29,6 +32,19 @@ void main() {
|
||||
createdAt: null,
|
||||
),
|
||||
],
|
||||
realms: const [
|
||||
LauncherRealm(
|
||||
id: 1,
|
||||
name: 'MoonWell',
|
||||
address: 'logon.moon-well.online',
|
||||
port: 8085,
|
||||
population: 0.5,
|
||||
gameBuild: 12340,
|
||||
online: true,
|
||||
status: 'online',
|
||||
),
|
||||
],
|
||||
account: const LauncherAccount(username: 'PLAYERONE'),
|
||||
),
|
||||
preferencesRepository: _FakePreferencesRepository(),
|
||||
session: LauncherSession(
|
||||
@@ -47,12 +63,18 @@ void main() {
|
||||
expect(bloc.state.model.newsErrorMessage, isNull);
|
||||
expect(bloc.state.model.newsItems, hasLength(1));
|
||||
expect(bloc.state.model.newsItems.first.title, 'Update 1.2');
|
||||
expect(bloc.state.model.realm?.name, 'MoonWell');
|
||||
expect(bloc.state.model.realm?.online, isTrue);
|
||||
expect(bloc.state.model.accountUsername, 'PLAYERONE');
|
||||
|
||||
await bloc.close();
|
||||
});
|
||||
|
||||
test('fetches a fresh manifest for every client sync', () async {
|
||||
test('starts sync on startup when output directory is saved', () async {
|
||||
final syncUseCase = _CapturingClientSyncUseCase();
|
||||
final manifest = ClientManifest.fromJson(<String, Object?>{
|
||||
'files': <Map<String, Object?>>[],
|
||||
});
|
||||
final bloc = HomeScreenBloc(
|
||||
clientSyncUseCase: syncUseCase,
|
||||
gameInstallationService: _FakeGameInstallationService(),
|
||||
@@ -65,17 +87,99 @@ void main() {
|
||||
tokenType: 'Bearer',
|
||||
expiresAt: null,
|
||||
),
|
||||
manifest: ClientManifest.fromJson(<String, Object?>{
|
||||
'files': <Map<String, Object?>>[],
|
||||
}),
|
||||
manifest: manifest,
|
||||
);
|
||||
|
||||
await pumpEventQueue(times: 20);
|
||||
bloc.add(HomeScreenSyncRequested());
|
||||
|
||||
expect(syncUseCase.inputs, hasLength(1));
|
||||
expect(syncUseCase.inputs.single.request.manifest, same(manifest));
|
||||
|
||||
await bloc.close();
|
||||
});
|
||||
|
||||
test('refreshes manifest and syncs when build changes', () async {
|
||||
final startupManifest = ClientManifest.fromJson(<String, Object?>{
|
||||
'files': <Map<String, Object?>>[],
|
||||
});
|
||||
final updatedManifest = ClientManifest.fromJson(<String, Object?>{
|
||||
'files': <Map<String, Object?>>[
|
||||
<String, Object?>{
|
||||
'path': 'Wow.exe',
|
||||
'size': 42,
|
||||
'sha256':
|
||||
'0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef',
|
||||
},
|
||||
],
|
||||
});
|
||||
final syncUseCase = _CapturingClientSyncUseCase();
|
||||
final apiClient = _FakeLauncherApiClient(
|
||||
newsItems: const [],
|
||||
manifest: updatedManifest,
|
||||
);
|
||||
final bloc = HomeScreenBloc(
|
||||
clientSyncUseCase: syncUseCase,
|
||||
gameInstallationService: _FakeGameInstallationService(),
|
||||
launcherApiClient: apiClient,
|
||||
preferencesRepository: _FakePreferencesRepository(
|
||||
outputDir: Uri.directory('C:/World of Warcraft'),
|
||||
),
|
||||
session: LauncherSession(
|
||||
accessToken: 'token',
|
||||
tokenType: 'Bearer',
|
||||
expiresAt: null,
|
||||
),
|
||||
manifest: startupManifest,
|
||||
);
|
||||
|
||||
await pumpEventQueue(times: 20);
|
||||
bloc.add(HomeScreenManifestRefreshRequested());
|
||||
await pumpEventQueue(times: 20);
|
||||
|
||||
expect(syncUseCase.lastInput, isNotNull);
|
||||
expect(syncUseCase.lastInput!.request.manifest, isNull);
|
||||
expect(apiClient.manifestRequestCount, 1);
|
||||
expect(syncUseCase.inputs, hasLength(2));
|
||||
expect(syncUseCase.inputs.last.request.manifest, same(updatedManifest));
|
||||
|
||||
await bloc.close();
|
||||
});
|
||||
|
||||
test('returns to ready state after launched game exits', () async {
|
||||
final manifest = ClientManifest.fromJson(<String, Object?>{
|
||||
'files': <Map<String, Object?>>[],
|
||||
});
|
||||
final gameService = _TrackingGameInstallationService();
|
||||
final bloc = HomeScreenBloc(
|
||||
clientSyncUseCase: _CapturingClientSyncUseCase(),
|
||||
gameInstallationService: gameService,
|
||||
launcherApiClient: _FakeLauncherApiClient(newsItems: const []),
|
||||
preferencesRepository: _FakePreferencesRepository(
|
||||
outputDir: Uri.directory('C:/World of Warcraft'),
|
||||
),
|
||||
session: LauncherSession(
|
||||
accessToken: 'token',
|
||||
tokenType: 'Bearer',
|
||||
expiresAt: null,
|
||||
),
|
||||
manifest: manifest,
|
||||
);
|
||||
|
||||
await pumpEventQueue(times: 20);
|
||||
bloc.add(HomeScreenPlayRequested());
|
||||
await pumpEventQueue(times: 20);
|
||||
|
||||
expect(gameService.launchCount, 1);
|
||||
expect(bloc.state.model.isGameRunning, isTrue);
|
||||
expect(bloc.state.model.canPlay, isFalse);
|
||||
|
||||
gameService.exitCode.complete(0);
|
||||
await pumpEventQueue(times: 20);
|
||||
|
||||
expect(bloc.state.model.isGameRunning, isFalse);
|
||||
expect(bloc.state.model.canPlay, isTrue);
|
||||
expect(
|
||||
bloc.state.model.statusText,
|
||||
'Игра закрыта. Клиент готов к запуску.',
|
||||
);
|
||||
|
||||
await bloc.close();
|
||||
});
|
||||
@@ -96,23 +200,59 @@ class _IdleClientSyncUseCase extends ClientSyncUseCase {
|
||||
}
|
||||
|
||||
class _CapturingClientSyncUseCase extends _IdleClientSyncUseCase {
|
||||
ClientSyncUseCaseInput? lastInput;
|
||||
final List<ClientSyncUseCaseInput> inputs = [];
|
||||
|
||||
@override
|
||||
Stream<ClientSyncStatus> call(ClientSyncUseCaseInput input) {
|
||||
lastInput = input;
|
||||
return const Stream<ClientSyncStatus>.empty();
|
||||
inputs.add(input);
|
||||
final manifest = input.request.manifest;
|
||||
return Stream<ClientSyncStatus>.value(
|
||||
ClientSyncStatus(
|
||||
stage: ClientSyncStage.completed,
|
||||
progress: const DownloadProgress.initial(),
|
||||
message: 'Клиент обновлён.',
|
||||
currentPath: null,
|
||||
localBuildHash: manifest?.buildHash,
|
||||
remoteBuildHash: manifest?.buildHash,
|
||||
processedFiles: manifest?.files.length ?? 0,
|
||||
totalFiles: manifest?.files.length ?? 0,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _FakeLauncherApiClient extends LauncherApiClient {
|
||||
_FakeLauncherApiClient({required this.newsItems});
|
||||
_FakeLauncherApiClient({
|
||||
required this.newsItems,
|
||||
this.manifest,
|
||||
this.realms = const [],
|
||||
this.account = const LauncherAccount(username: ''),
|
||||
});
|
||||
|
||||
final List<LauncherNewsItem> newsItems;
|
||||
final ClientManifest? manifest;
|
||||
final List<LauncherRealm> realms;
|
||||
final LauncherAccount account;
|
||||
int manifestRequestCount = 0;
|
||||
|
||||
@override
|
||||
Future<List<LauncherNewsItem>> fetchNews(String accessToken) async =>
|
||||
newsItems;
|
||||
|
||||
@override
|
||||
Future<List<LauncherRealm>> fetchRealms() async => realms;
|
||||
|
||||
@override
|
||||
Future<LauncherAccount> fetchAccount(String accessToken) async => account;
|
||||
|
||||
@override
|
||||
Future<ClientManifest> fetchManifest(String accessToken) async {
|
||||
manifestRequestCount += 1;
|
||||
return manifest ??
|
||||
ClientManifest.fromJson(<String, Object?>{
|
||||
'files': <Map<String, Object?>>[],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class _FakeGameInstallationService extends GameInstallationService {
|
||||
@@ -129,6 +269,23 @@ class _FakeGameInstallationService extends GameInstallationService {
|
||||
}
|
||||
}
|
||||
|
||||
class _TrackingGameInstallationService extends _FakeGameInstallationService {
|
||||
final Completer<int> exitCode = Completer<int>();
|
||||
int launchCount = 0;
|
||||
|
||||
@override
|
||||
Future<bool> hasClientExecutable(String installationDir) async => true;
|
||||
|
||||
@override
|
||||
Future<void> clearCache(String installationDir) async {}
|
||||
|
||||
@override
|
||||
Future<GameProcessHandle> launchGame(String installationDir) async {
|
||||
launchCount += 1;
|
||||
return GameProcessHandle(pid: 42, exitCode: exitCode.future);
|
||||
}
|
||||
}
|
||||
|
||||
class _FakePreferencesRepository implements PreferencesRepository {
|
||||
_FakePreferencesRepository({this.outputDir});
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ void main() {
|
||||
.toList();
|
||||
|
||||
expect(statuses.last.stage, ClientSyncStage.completed);
|
||||
expect(statuses.last.message, 'Client is up to date.');
|
||||
expect(statuses.last.message, 'Клиент обновлён.');
|
||||
expect(api.downloadedPaths, isEmpty);
|
||||
expect(installationService.deletedRelativeFiles, isEmpty);
|
||||
expect(logger.errorMessages, isEmpty);
|
||||
@@ -139,7 +139,7 @@ void main() {
|
||||
);
|
||||
expect(installationService.deletedRelativeFiles, ['legacy.txt']);
|
||||
expect(api.downloadedPaths, ['Wow.exe', 'Data/common.MPQ']);
|
||||
expect(statuses.last.message, 'Client is ready to play.');
|
||||
expect(statuses.last.message, 'Клиент готов к игре.');
|
||||
expect(logger.errorMessages, isEmpty);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:moonwell_launcher/features/launcher/data/launcher_news_link.dart';
|
||||
|
||||
void main() {
|
||||
test('builds public news URL from launcher base URL and news id', () {
|
||||
expect(
|
||||
buildLauncherNewsUri('http://localhost:8080', 4),
|
||||
Uri.parse('http://localhost:8080/news/4'),
|
||||
);
|
||||
});
|
||||
|
||||
test('preserves a base URL path when building news URL', () {
|
||||
expect(
|
||||
buildLauncherNewsUri('https://example.com/moonwell', 7),
|
||||
Uri.parse('https://example.com/moonwell/news/7'),
|
||||
);
|
||||
});
|
||||
|
||||
test('rejects invalid base URLs and news ids', () {
|
||||
expect(buildLauncherNewsUri('', 4), isNull);
|
||||
expect(buildLauncherNewsUri('not-a-url', 4), isNull);
|
||||
expect(buildLauncherNewsUri('https://example.com', 0), isNull);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:moonwell_launcher/features/launcher/domain/entities/launcher_account.dart';
|
||||
|
||||
void main() {
|
||||
test('LauncherAccount parses username without exposing balance', () {
|
||||
final account = LauncherAccount.fromJson(<String, Object?>{
|
||||
'username': 'PLAYERONE',
|
||||
'balance': '1250.00',
|
||||
});
|
||||
|
||||
expect(account.username, 'PLAYERONE');
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:moonwell_launcher/features/launcher/domain/entities/launcher_realm.dart';
|
||||
|
||||
void main() {
|
||||
test('LauncherRealm parses launcher realms payload', () {
|
||||
final realm = LauncherRealm.fromJson(<String, Object?>{
|
||||
'id': 1,
|
||||
'name': 'MoonWell',
|
||||
'address': 'logon.moon-well.online',
|
||||
'port': 8085,
|
||||
'population': 0.5,
|
||||
'game_build': 12340,
|
||||
'online': true,
|
||||
'status': 'online',
|
||||
});
|
||||
|
||||
expect(realm.id, 1);
|
||||
expect(realm.name, 'MoonWell');
|
||||
expect(realm.address, 'logon.moon-well.online');
|
||||
expect(realm.port, 8085);
|
||||
expect(realm.population, 0.5);
|
||||
expect(realm.gameBuild, 12340);
|
||||
expect(realm.online, isTrue);
|
||||
expect(realm.status, 'online');
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user