Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions lib/pages/advanced/xray/controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,21 @@ class XrayRuntimeController extends PageCubit<XrayRuntimePageState> {
}
}

Future<void> refreshPingSettings() async {
try {
final preferences = PingState();
await preferences.readFromPreferences();
emit(
state.copyWith(
pingTimeout: preferences.timeout,
pingUrl: preferences.url,
),
);
} catch (error) {
emit(state.copyWith(failed: true, failure: error));
}
}

void setLog(String key, Object value) {
if (busy || state.systemExtension) return;
emit(state.copyWith(log: {...log, key: value}));
Expand Down
9 changes: 7 additions & 2 deletions lib/pages/advanced/xray/page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class XrayRuntimePage extends StatelessWidget {
});
final void Function(BuildContext) onGeodata;
final void Function(BuildContext) onUpdates;
final void Function(BuildContext) onSpeedTest;
final Future<void> Function(BuildContext) onSpeedTest;
final void Function(BuildContext, ConfigFileViewerParams) onConfig;
final void Function(BuildContext)? onDiagnostics;
final Future<bool?> Function(BuildContext)? onLogSettings;
Expand Down Expand Up @@ -180,7 +180,12 @@ class XrayRuntimePage extends StatelessWidget {
: _chevron(context),
),
enabled: true,
onTap: () => onSpeedTest(context),
onTap: () async {
await onSpeedTest(context);
if (controller.isPageActive) {
await controller.refreshPingSettings();
}
},
),
if (onServerDialing != null)
_pushRow(
Expand Down
2 changes: 1 addition & 1 deletion test/pages/advanced/xray/dialing_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ void main() {
createController: () => runtime,
onGeodata: (_) {},
onUpdates: (_) {},
onSpeedTest: (_) {},
onSpeedTest: (_) async {},
onConfig: (_, _) {},
onServerDialing: (context) {
opened = true;
Expand Down
2 changes: 1 addition & 1 deletion test/pages/advanced/xray/lan_proxy_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ void main() {
createController: () => controller,
onGeodata: (_) {},
onUpdates: (_) {},
onSpeedTest: (_) {},
onSpeedTest: (_) async {},
onConfig: (_, _) {},
onLanProxy: (_) async {},
),
Expand Down
2 changes: 1 addition & 1 deletion test/pages/advanced/xray/log_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ void main() {
onDiagnostics: (_) {},
onGeodata: (_) {},
onUpdates: (_) {},
onSpeedTest: (_) {},
onSpeedTest: (_) async {},
onConfig: (_, _) {},
onLogSettings: (_) async => null,
),
Expand Down
209 changes: 152 additions & 57 deletions test/pages/advanced/xray/settings_ui_test.dart
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
import 'dart:io';

import 'package:drift/native.dart';
import 'package:flutter/services.dart';
import 'package:material_ui/material_ui.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:go_router/go_router.dart';
import 'package:onexray/core/constants/preferences.dart';
import 'package:onexray/core/db/database/database.dart';
import 'package:onexray/core/pigeon/messages.g.dart';
import 'package:onexray/l10n/localizations/app_localizations.dart';
import 'package:onexray/service/settings/language/locale.dart';
import 'package:onexray/pages/advanced/xray/controller.dart';
import 'package:onexray/pages/advanced/xray/log/controller.dart';
import 'package:onexray/pages/advanced/xray/log/params.dart';
import 'package:onexray/pages/advanced/xray/page.dart';
import 'package:onexray/pages/advanced/xray/ping/page.dart';
import 'package:onexray/pages/advanced/xray/ping/controller.dart';
import 'package:onexray/pages/shared/widgets/page_action_bar.dart';
Expand Down Expand Up @@ -164,68 +168,159 @@ void main() {
});

for (final save in [false, true]) {
testWidgets('ping URL ${save ? 'saves' : 'cancels'} its draft', (
tester,
) async {
await (PingState()
..url = PingUrl.custom
..customUrl = 'https://example.com/old'
..timeout = 7)
.saveToPreferences();
final router = GoRouter(
routes: [
GoRoute(
path: '/',
builder: (context, _) => Scaffold(
body: TextButton(
onPressed: () => context.push('/ping'),
child: const Text('Open ping settings'),
testWidgets(
'ping settings ${save ? 'save and refresh' : 'cancel and retain'} the Xray summary',
(tester) async {
await tester.binding.setSurfaceSize(const Size(1160, 900));
addTearDown(() => tester.binding.setSurfaceSize(null));
await (PingState()
..url = PingUrl.google
..customUrl = 'https://example.com/old'
..timeout = 5)
.saveToPreferences();
const systemExtensionChannel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.onexray.BridgeHostApi.useSystemExtension',
BridgeHostApi.pigeonChannelCodec,
);
final messenger = tester.binding.defaultBinaryMessenger;
messenger.setMockDecodedMessageHandler(
systemExtensionChannel,
(_) async => [false],
);
addTearDown(
() => messenger.setMockDecodedMessageHandler(
systemExtensionChannel,
null,
),
);
final db = AppDatabase.forTesting(NativeDatabase.memory());
final coordinator = ConnectionCoordinator(
database: db,
readRuntime: () async => null,
);
late XrayRuntimeController root;
await tester.runAsync(() async {
root = XrayRuntimeController(coordinator: coordinator);
await root.stream.firstWhere((state) => !state.loading);
});
addTearDown(
() => tester.runAsync(() async {
await root.close();
coordinator.dispose();
await db.close();
}),
);
expect(root.failed, isFalse);
final router = GoRouter(
routes: [
GoRoute(
path: '/',
builder: (context, _) => XrayRuntimePage(
createController: () => root,
onGeodata: (_) {},
onUpdates: (_) {},
onSpeedTest: (context) => context.push<void>('/ping'),
onConfig: (_, _) {},
),
),
GoRoute(path: '/ping', builder: (_, _) => const PingPage()),
],
);
addTearDown(router.dispose);
await tester.pumpWidget(
MaterialApp.router(
theme: AppTheme.light,
locale: const Locale('en'),
localizationsDelegates: AppLocalePolicy.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
routerConfig: router,
builder: (_, child) => ShadTheme(
data: AppTheme.shad(Brightness.light),
child: ShadToaster(child: child!),
),
),
GoRoute(path: '/ping', builder: (_, _) => const PingPage()),
],
);
addTearDown(router.dispose);
await tester.pumpWidget(
MaterialApp.router(
theme: AppTheme.light,
locale: const Locale('en'),
localizationsDelegates: AppLocalePolicy.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
routerConfig: router,
builder: (_, child) => ShadTheme(
data: AppTheme.shad(Brightness.light),
child: ShadToaster(child: child!),
),
),
);
await tester.tap(find.text('Open ping settings'));
await tester.pumpAndSettle();
await tester.ensureVisible(find.byType(TextField));
await tester.enterText(find.byType(TextField), 'https://example.com/new');
await tester.pumpAndSettle();
final storedDraft = PingState();
await storedDraft.readFromPreferences();
expect(storedDraft.customUrl, 'https://example.com/old');
);
await tester.pumpAndSettle();
final l = AppLocalizations.of(
tester.element(find.byType(XrayRuntimePage)),
)!;
final originalSummary = '${l.prototypeSeconds(5)} · Google';
final savedSummary =
'${l.prototypeSeconds(3)} · ${l.prototypeCustomUrl}';
expect(find.text(originalSummary), findsOneWidget);
await tester.ensureVisible(find.text(l.prototypeSpeedTest));
await tester.tap(find.text(l.prototypeSpeedTest));
await tester.pumpAndSettle();
await tester.tap(find.byKey(const ValueKey('ping-url-preset')));
await tester.pumpAndSettle();
await tester.tap(find.text(l.prototypeCustom).last);
await tester.pumpAndSettle();
await tester.tap(find.byKey(const ValueKey('ping-timeout')));
await tester.pumpAndSettle();
await tester.tap(find.text(l.prototypeSeconds(3)).last);
await tester.pumpAndSettle();
await tester.ensureVisible(find.byType(TextField));
await tester.enterText(
find.byType(TextField),
'https://example.com/new',
);
await tester.pumpAndSettle();
final storedDraft = PingState();
await storedDraft.readFromPreferences();
expect(storedDraft.customUrl, 'https://example.com/old');
expect(storedDraft.url, PingUrl.google);
expect(storedDraft.timeout, 5);

await tester.tap(find.text(save ? 'Save' : 'Cancel'));
await tester.pumpAndSettle();
expect(find.byType(PingPage), findsNothing);
final restored = PingState();
await restored.readFromPreferences();
expect(restored.customUrl, 'https://example.com/${save ? 'new' : 'old'}');
expect(restored.url, PingUrl.custom);
expect(restored.timeout, 7);
await tester.tap(find.text(save ? l.prototypeSave : l.prototypeCancel));
await tester.pumpAndSettle();
expect(find.byType(PingPage), findsNothing);
final restored = PingState();
await restored.readFromPreferences();
expect(
restored.customUrl,
'https://example.com/${save ? 'new' : 'old'}',
);
expect(restored.url, save ? PingUrl.custom : PingUrl.google);
expect(restored.timeout, save ? 3 : 5);
expect(
find.text(save ? savedSummary : originalSummary),
findsOneWidget,
);

await tester.tap(find.text('Open ping settings'));
await tester.pumpAndSettle();
expect(
tester.widget<TextField>(find.byType(TextField)).controller!.text,
restored.customUrl,
);
expect(tester.takeException(), isNull);
});
await tester.tap(find.text(l.prototypeSpeedTest));
await tester.pumpAndSettle();
expect(
tester
.widget<SettingSelect<String>>(
find.byKey(const ValueKey('ping-url-preset')),
)
.value,
restored.url.name,
);
expect(
tester
.widget<SettingSelect<double>>(
find.byKey(const ValueKey('ping-timeout')),
)
.value,
restored.timeout,
);
if (!save) {
await tester.tap(find.byKey(const ValueKey('ping-url-preset')));
await tester.pumpAndSettle();
await tester.tap(find.text(l.prototypeCustom).last);
await tester.pumpAndSettle();
}
expect(
tester.widget<TextField>(find.byType(TextField)).controller!.text,
restored.customUrl,
);
expect(tester.takeException(), isNull);
await tester.runAsync(root.close);
await tester.pumpWidget(const SizedBox());
await tester.pumpAndSettle();
},
);
}

for (final locale in AppLocalizations.supportedLocales) {
Expand Down
2 changes: 1 addition & 1 deletion test/pages/connect/routing/custom_ui_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,7 @@ void main() {
expect(controller.profileState.directDnsAddress, '1.1.1.1');
final storedDns = jsonDecode(controller.previewState!.encode())['dns'];
expect(storedDns['servers'], [
{'tag': 'app-dns-proxy', 'address': '8.8.8.8'},
{'tag': 'app-dns-proxy', 'address': 'tcp://8.8.8.8'},
{'tag': 'app-dns-direct', 'address': '1.1.1.1'},
{'tag': 'app-dns-fake', 'address': 'fakedns'},
]);
Expand Down
6 changes: 3 additions & 3 deletions test/pages/connect/routing/options_form_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ void main() {
await tester.pumpAndSettle();
expect(
value.effectiveProxyResolvers.map((resolver) => resolver.address),
['8.8.8.8', 'https://dns.example/dns-query', '9.9.9.9'],
['tcp://8.8.8.8', 'https://dns.example/dns-query', '9.9.9.9'],
);
final delete = find.descendant(
of: extraFallback,
Expand All @@ -196,9 +196,9 @@ void main() {
final restored = RoutingDnsSettings.fromJson(saved!);
expect(
restored.effectiveProxyResolvers.map((resolver) => resolver.address),
['8.8.8.8', 'https://dns.example/dns-query'],
['tcp://8.8.8.8', 'https://dns.example/dns-query'],
);
expect(restored.directAddress, '8.8.8.8');
expect(restored.directAddress, 'tcp://8.8.8.8');
expect(restored.sameExitOnly, true);
expect(restored.disableCache, true);
expect(find.byType(TextField), findsNWidgets(3));
Expand Down
2 changes: 1 addition & 1 deletion test/service/advanced/local_api/configuration_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -523,7 +523,7 @@ void main() {
expect(compiled['env']['xray.location.asset'], VpnConstants.datDir);
expect(compiled.containsKey('geodata'), false);
if (kind == 'advanced-routing') {
expect(compiled['dns']['servers'].single['address'], '8.8.8.8');
expect(compiled['dns']['servers'].single['address'], 'tcp://8.8.8.8');
}
}
},
Expand Down
6 changes: 3 additions & 3 deletions test/service/connect/full_config_runtime_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ void main() {
final runtime = compiled.config;
_fixture('${provider ? 'provider' : 'raw'}-$dnsCase', compiled);
expect(runtime['dns']['servers'], [
for (final address in [_ipv4, _ipv6])
for (final address in ['tcp://$_ipv4', 'tcp://[$_ipv6]'])
{
'address': address,
'tag': VpnConfigAdapter.defaultDnsTag,
Expand Down Expand Up @@ -357,8 +357,8 @@ void main() {
final config = compiled.config;
final servers = config['dns']['servers'] as List;
expect(servers.map((value) => value['address']).toList(), [
_ipv4,
if (ipv6) _ipv6,
'tcp://$_ipv4',
if (ipv6) 'tcp://[$_ipv6]',
]);
expect(
servers.every(
Expand Down
3 changes: 2 additions & 1 deletion test/service/connect/routing/custom/service_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ void main() {
'tag': 'dnsOut',
'protocol': 'dns',
'settings': {
'rewriteNetwork': 'tcp',
'rules': [
{'action': 'hijack', 'qType': '1,28'},
{'action': 'direct'},
Expand Down Expand Up @@ -224,7 +225,7 @@ void main() {
'disableCache': false,
'disableFallbackIfMatch': false,
'servers': [
{'tag': 'app-dns-proxy', 'address': '8.8.8.8'},
{'tag': 'app-dns-proxy', 'address': 'tcp://8.8.8.8'},
{'tag': 'app-dns-direct', 'address': '9.9.9.9'},
],
});
Expand Down
Loading