feat: 优化 H5 线路切换逻辑;实现独立 WebView 管理和状态控制
This commit is contained in:
@@ -8,7 +8,7 @@ https://h5-test.imharry.work/
|
|||||||
|
|
||||||
## H5 线路切换
|
## H5 线路切换
|
||||||
|
|
||||||
线路切换在 Flutter 套壳层完成,H5 页面不需要承载线路切换逻辑。每条线路都是一个独立 H5 地址,切换时 WebView 会直接加载被选中的地址。
|
线路切换在 Flutter 套壳层完成,H5 页面不需要承载线路切换逻辑。每条线路对应一个独立 WebView,切换时只切换当前显示的 WebView,不会改写 H5 页面运行中的请求地址。
|
||||||
|
|
||||||
默认线路配置在:
|
默认线路配置在:
|
||||||
|
|
||||||
|
|||||||
@@ -76,29 +76,11 @@ class AppConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool shouldRewriteMainFrameUrl(String url) {
|
static bool shouldRewriteMainFrameUrl(String url) {
|
||||||
final uri = Uri.tryParse(url);
|
|
||||||
if (uri == null) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
final host = uri.host.toLowerCase();
|
|
||||||
return (legacyWebHosts.contains(host) && !_isConfiguredH5Host(host)) ||
|
|
||||||
_hasShellParams(uri);
|
|
||||||
}
|
|
||||||
|
|
||||||
static String canonicalizeMainFrameUrl(String url) {
|
static String canonicalizeMainFrameUrl(String url) {
|
||||||
final uri = _removeShellParams(Uri.parse(url));
|
return url;
|
||||||
final host = uri.host.toLowerCase();
|
|
||||||
if (!legacyWebHosts.contains(host) || _isConfiguredH5Host(host)) {
|
|
||||||
return uri.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
return uri
|
|
||||||
.replace(
|
|
||||||
scheme: 'https',
|
|
||||||
host: canonicalWebHost,
|
|
||||||
)
|
|
||||||
.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int? lineIndexForUrl(String url) {
|
static int? lineIndexForUrl(String url) {
|
||||||
@@ -134,16 +116,6 @@ class AppConfig {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool _hasShellParams(Uri uri) {
|
|
||||||
final fragmentParameters = Uri.splitQueryString(uri.fragment);
|
|
||||||
return uri.queryParameters.containsKey('flutter_shell') ||
|
|
||||||
uri.queryParameters.containsKey('shell_cache_bust') ||
|
|
||||||
uri.queryParameters.containsKey('shell_app_name') ||
|
|
||||||
uri.queryParameters.containsKey('shell_app_logo') ||
|
|
||||||
fragmentParameters.containsKey('shell_app_name') ||
|
|
||||||
fragmentParameters.containsKey('shell_app_logo');
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _safeLineIndex(int index, int length) {
|
static int _safeLineIndex(int index, int length) {
|
||||||
if (length <= 0 || index < 0 || index >= length) {
|
if (length <= 0 || index < 0 || index >= length) {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -151,10 +123,6 @@ class AppConfig {
|
|||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool _isConfiguredH5Host(String host) {
|
|
||||||
return h5Lines.any((line) => line.uri.host.toLowerCase() == host);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool _isSameLine(Uri lineUri, Uri uri) {
|
static bool _isSameLine(Uri lineUri, Uri uri) {
|
||||||
if (lineUri.host.toLowerCase() != uri.host.toLowerCase()) {
|
if (lineUri.host.toLowerCase() != uri.host.toLowerCase()) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
210
lib/main.dart
210
lib/main.dart
@@ -93,67 +93,68 @@ class H5ShellPage extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _H5ShellPageState extends State<H5ShellPage> {
|
class _H5ShellPageState extends State<H5ShellPage> {
|
||||||
late final WebViewController _controller;
|
|
||||||
late final List<H5Line> _h5Lines;
|
late final List<H5Line> _h5Lines;
|
||||||
|
late final List<_H5LineWebViewSlot> _lineSlots;
|
||||||
|
|
||||||
int _progress = 0;
|
|
||||||
String? _loadError;
|
|
||||||
bool _showShellCover = true;
|
|
||||||
bool _shellBrandingLoaded = false;
|
bool _shellBrandingLoaded = false;
|
||||||
int _currentLineIndex = 0;
|
int _currentLineIndex = 0;
|
||||||
Timer? _shellCoverFallbackTimer;
|
|
||||||
late ShellBranding _shellBranding;
|
late ShellBranding _shellBranding;
|
||||||
|
|
||||||
H5Line get _currentLine => _h5Lines[_currentLineIndex];
|
H5Line get _currentLine => _h5Lines[_currentLineIndex];
|
||||||
|
_H5LineWebViewSlot get _currentSlot => _lineSlots[_currentLineIndex];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_h5Lines = AppConfig.h5Lines;
|
_h5Lines = AppConfig.h5Lines;
|
||||||
_shellBranding = widget.initialShellBranding;
|
_shellBranding = widget.initialShellBranding;
|
||||||
_controller = _buildController();
|
_lineSlots = [
|
||||||
unawaited(_loadHome());
|
for (var index = 0; index < _h5Lines.length; index += 1)
|
||||||
|
_H5LineWebViewSlot(
|
||||||
|
line: _h5Lines[index],
|
||||||
|
controller: _buildController(index),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
unawaited(_ensureLineLoaded(_currentLineIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
WebViewController _buildController() {
|
WebViewController _buildController(int lineIndex) {
|
||||||
return WebViewController()
|
return WebViewController()
|
||||||
..setJavaScriptMode(JavaScriptMode.unrestricted)
|
..setJavaScriptMode(JavaScriptMode.unrestricted)
|
||||||
..addJavaScriptChannel(
|
..addJavaScriptChannel(
|
||||||
'OpenIMShell',
|
'OpenIMShell',
|
||||||
onMessageReceived: _handleShellMessage,
|
onMessageReceived: (message) => _handleShellMessage(lineIndex, message),
|
||||||
)
|
)
|
||||||
..setBackgroundColor(_shellBackground)
|
..setBackgroundColor(_shellBackground)
|
||||||
..setNavigationDelegate(
|
..setNavigationDelegate(
|
||||||
NavigationDelegate(
|
NavigationDelegate(
|
||||||
onProgress: (progress) {
|
onProgress: (progress) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() => _progress = progress);
|
setState(() => _lineSlots[lineIndex].progress = progress);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onPageStarted: (url) {
|
onPageStarted: (_) {
|
||||||
_shellCoverFallbackTimer?.cancel();
|
final slot = _lineSlots[lineIndex];
|
||||||
final lineIndex = AppConfig.lineIndexForUrl(url);
|
slot.shellCoverFallbackTimer?.cancel();
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
if (lineIndex != null) {
|
slot.loadError = null;
|
||||||
_currentLineIndex = lineIndex;
|
slot.progress = 0;
|
||||||
}
|
slot.showShellCover = true;
|
||||||
_loadError = null;
|
|
||||||
_progress = 0;
|
|
||||||
_showShellCover = true;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onPageFinished: (_) {
|
onPageFinished: (_) {
|
||||||
unawaited(_handlePageFinished());
|
unawaited(_handlePageFinished(lineIndex));
|
||||||
},
|
},
|
||||||
onWebResourceError: (error) {
|
onWebResourceError: (error) {
|
||||||
if (error.isForMainFrame ?? true) {
|
if (error.isForMainFrame ?? true) {
|
||||||
_shellCoverFallbackTimer?.cancel();
|
final slot = _lineSlots[lineIndex];
|
||||||
|
slot.shellCoverFallbackTimer?.cancel();
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_loadError = error.description;
|
slot.loadError = error.description;
|
||||||
_showShellCover = false;
|
slot.showShellCover = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -165,63 +166,63 @@ class _H5ShellPageState extends State<H5ShellPage> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_shellCoverFallbackTimer?.cancel();
|
for (final slot in _lineSlots) {
|
||||||
|
slot.dispose();
|
||||||
|
}
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _runJavaScriptSafely(String source) async {
|
Future<void> _runJavaScriptSafely(int lineIndex, String source) async {
|
||||||
try {
|
try {
|
||||||
await _controller.runJavaScript(source);
|
await _lineSlots[lineIndex].controller.runJavaScript(source);
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
// WebView can reject JavaScript while a page is still navigating.
|
// WebView can reject JavaScript while a page is still navigating.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String _freshHomeUrl() {
|
Future<void> _handlePageFinished(int lineIndex) async {
|
||||||
return AppConfig.homeUrl(lineIndex: _currentLineIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _handlePageFinished() async {
|
|
||||||
await _loadShellBrandingIfNeeded();
|
await _loadShellBrandingIfNeeded();
|
||||||
await _syncShellBranding();
|
await _syncShellBranding(lineIndex);
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_progress = 100;
|
_lineSlots[lineIndex].progress = 100;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
_scheduleShellCoverFallback();
|
_scheduleShellCoverFallback(lineIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleShellMessage(JavaScriptMessage message) {
|
void _handleShellMessage(int lineIndex, JavaScriptMessage message) {
|
||||||
try {
|
try {
|
||||||
final decoded = jsonDecode(message.message);
|
final decoded = jsonDecode(message.message);
|
||||||
if (decoded is Map && decoded['type'] == 'first-screen-ready') {
|
if (decoded is Map && decoded['type'] == 'first-screen-ready') {
|
||||||
_hideShellCover();
|
_hideShellCover(lineIndex);
|
||||||
}
|
}
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
if (message.message == 'first-screen-ready') {
|
if (message.message == 'first-screen-ready') {
|
||||||
_hideShellCover();
|
_hideShellCover(lineIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _scheduleShellCoverFallback() {
|
void _scheduleShellCoverFallback(int lineIndex) {
|
||||||
_shellCoverFallbackTimer?.cancel();
|
final slot = _lineSlots[lineIndex];
|
||||||
_shellCoverFallbackTimer = Timer(
|
slot.shellCoverFallbackTimer?.cancel();
|
||||||
|
slot.shellCoverFallbackTimer = Timer(
|
||||||
const Duration(seconds: 4),
|
const Duration(seconds: 4),
|
||||||
_hideShellCover,
|
() => _hideShellCover(lineIndex),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _hideShellCover() {
|
void _hideShellCover(int lineIndex) {
|
||||||
_shellCoverFallbackTimer?.cancel();
|
final slot = _lineSlots[lineIndex];
|
||||||
if (!mounted || !_showShellCover) {
|
slot.shellCoverFallbackTimer?.cancel();
|
||||||
|
if (!mounted || !slot.showShellCover) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_progress = 100;
|
slot.progress = 100;
|
||||||
_showShellCover = false;
|
slot.showShellCover = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -242,7 +243,7 @@ class _H5ShellPageState extends State<H5ShellPage> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _syncShellBranding() {
|
Future<void> _syncShellBranding(int lineIndex) {
|
||||||
final payload = jsonEncode({
|
final payload = jsonEncode({
|
||||||
'name': _shellBranding.appName,
|
'name': _shellBranding.appName,
|
||||||
'logo': _shellBranding.appLogo,
|
'logo': _shellBranding.appLogo,
|
||||||
@@ -256,30 +257,44 @@ class _H5ShellPageState extends State<H5ShellPage> {
|
|||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
})();
|
})();
|
||||||
''';
|
''';
|
||||||
return _runJavaScriptSafely(script);
|
return _runJavaScriptSafely(lineIndex, script);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _loadHome() async {
|
Future<void> _ensureLineLoaded(int lineIndex) async {
|
||||||
await _loadUrl(_freshHomeUrl());
|
final slot = _lineSlots[lineIndex];
|
||||||
|
if (slot.hasLoadedInitialRequest) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _loadUrl(String url) async {
|
slot.hasLoadedInitialRequest = true;
|
||||||
final targetUrl = AppConfig.canonicalizeMainFrameUrl(url);
|
|
||||||
final lineIndex = AppConfig.lineIndexForUrl(targetUrl);
|
|
||||||
|
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
_shellCoverFallbackTimer?.cancel();
|
slot.shellCoverFallbackTimer?.cancel();
|
||||||
setState(() {
|
setState(() {
|
||||||
if (lineIndex != null) {
|
slot.loadError = null;
|
||||||
_currentLineIndex = lineIndex;
|
slot.progress = 0;
|
||||||
}
|
slot.showShellCover = true;
|
||||||
_loadError = null;
|
|
||||||
_progress = 0;
|
|
||||||
_showShellCover = true;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await _controller.loadRequest(Uri.parse(targetUrl));
|
await slot.controller.loadRequest(Uri.parse(slot.line.url));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _reloadCurrentLine() async {
|
||||||
|
final slot = _currentSlot;
|
||||||
|
slot.shellCoverFallbackTimer?.cancel();
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {
|
||||||
|
slot.loadError = null;
|
||||||
|
slot.progress = 0;
|
||||||
|
slot.showShellCover = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (slot.hasLoadedInitialRequest) {
|
||||||
|
await slot.controller.reload();
|
||||||
|
} else {
|
||||||
|
await _ensureLineLoaded(_currentLineIndex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _loadLine(int index) async {
|
Future<void> _loadLine(int index) async {
|
||||||
@@ -290,7 +305,7 @@ class _H5ShellPageState extends State<H5ShellPage> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await _loadUrl(_h5Lines[safeIndex].url);
|
await _ensureLineLoaded(safeIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _showLineSwitcher() {
|
void _showLineSwitcher() {
|
||||||
@@ -327,11 +342,6 @@ class _H5ShellPageState extends State<H5ShellPage> {
|
|||||||
|
|
||||||
const webSchemes = {'http', 'https', 'about', 'data'};
|
const webSchemes = {'http', 'https', 'about', 'data'};
|
||||||
if (webSchemes.contains(uri.scheme)) {
|
if (webSchemes.contains(uri.scheme)) {
|
||||||
if (request.isMainFrame &&
|
|
||||||
AppConfig.shouldRewriteMainFrameUrl(request.url)) {
|
|
||||||
unawaited(_loadUrl(AppConfig.canonicalizeMainFrameUrl(request.url)));
|
|
||||||
return NavigationDecision.prevent;
|
|
||||||
}
|
|
||||||
return NavigationDecision.navigate;
|
return NavigationDecision.navigate;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -345,8 +355,9 @@ class _H5ShellPageState extends State<H5ShellPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _handleBackNavigation() async {
|
Future<void> _handleBackNavigation() async {
|
||||||
if (await _controller.canGoBack()) {
|
final controller = _currentSlot.controller;
|
||||||
await _controller.goBack();
|
if (await controller.canGoBack()) {
|
||||||
|
await controller.goBack();
|
||||||
} else {
|
} else {
|
||||||
await SystemNavigator.pop();
|
await SystemNavigator.pop();
|
||||||
}
|
}
|
||||||
@@ -354,9 +365,11 @@ class _H5ShellPageState extends State<H5ShellPage> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final currentSlot = _currentSlot;
|
||||||
final bottomInset = MediaQuery.viewInsetsOf(context).bottom;
|
final bottomInset = MediaQuery.viewInsetsOf(context).bottom;
|
||||||
final showLineSwitch =
|
final showLineSwitch = !currentSlot.showShellCover &&
|
||||||
!_showShellCover && _loadError == null && bottomInset == 0;
|
currentSlot.loadError == null &&
|
||||||
|
bottomInset == 0;
|
||||||
|
|
||||||
return PopScope(
|
return PopScope(
|
||||||
canPop: false,
|
canPop: false,
|
||||||
@@ -371,23 +384,38 @@ class _H5ShellPageState extends State<H5ShellPage> {
|
|||||||
bottom: false,
|
bottom: false,
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
Positioned.fill(child: _ShellFallback(progress: _progress)),
|
Positioned.fill(
|
||||||
WebViewWidget(controller: _controller),
|
child: _ShellFallback(progress: currentSlot.progress),
|
||||||
if (_showShellCover)
|
),
|
||||||
|
Positioned.fill(
|
||||||
|
child: IndexedStack(
|
||||||
|
index: _currentLineIndex,
|
||||||
|
children: [
|
||||||
|
for (var index = 0; index < _lineSlots.length; index += 1)
|
||||||
|
WebViewWidget(
|
||||||
|
key: ValueKey<int>(index),
|
||||||
|
controller: _lineSlots[index].controller,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (currentSlot.showShellCover)
|
||||||
Positioned.fill(
|
Positioned.fill(
|
||||||
child: IgnorePointer(
|
child: IgnorePointer(
|
||||||
child: _ShellFallback(progress: _progress),
|
child: _ShellFallback(progress: currentSlot.progress),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (!_showShellCover && _progress < 100)
|
if (!currentSlot.showShellCover && currentSlot.progress < 100)
|
||||||
LinearProgressIndicator(
|
LinearProgressIndicator(
|
||||||
value: _progress == 0 ? null : _progress / 100,
|
value: currentSlot.progress == 0
|
||||||
|
? null
|
||||||
|
: currentSlot.progress / 100,
|
||||||
minHeight: 2,
|
minHeight: 2,
|
||||||
),
|
),
|
||||||
if (_loadError != null)
|
if (currentSlot.loadError != null)
|
||||||
_ErrorPanel(
|
_ErrorPanel(
|
||||||
message: _loadError!,
|
message: currentSlot.loadError!,
|
||||||
onRetry: () => unawaited(_loadHome()),
|
onRetry: () => unawaited(_reloadCurrentLine()),
|
||||||
),
|
),
|
||||||
if (showLineSwitch)
|
if (showLineSwitch)
|
||||||
Positioned(
|
Positioned(
|
||||||
@@ -407,6 +435,26 @@ class _H5ShellPageState extends State<H5ShellPage> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _H5LineWebViewSlot {
|
||||||
|
_H5LineWebViewSlot({
|
||||||
|
required this.line,
|
||||||
|
required this.controller,
|
||||||
|
});
|
||||||
|
|
||||||
|
final H5Line line;
|
||||||
|
final WebViewController controller;
|
||||||
|
|
||||||
|
int progress = 0;
|
||||||
|
String? loadError;
|
||||||
|
bool showShellCover = true;
|
||||||
|
bool hasLoadedInitialRequest = false;
|
||||||
|
Timer? shellCoverFallbackTimer;
|
||||||
|
|
||||||
|
void dispose() {
|
||||||
|
shellCoverFallbackTimer?.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class _ErrorPanel extends StatelessWidget {
|
class _ErrorPanel extends StatelessWidget {
|
||||||
const _ErrorPanel({required this.message, required this.onRetry});
|
const _ErrorPanel({required this.message, required this.onRetry});
|
||||||
|
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ void main() {
|
|||||||
isFalse);
|
isFalse);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('keeps independent non-legacy H5 hosts when removing shell params', () {
|
test('keeps H5 runtime URLs untouched during shell routing', () {
|
||||||
final uri = Uri.parse(
|
final uri = Uri.parse(
|
||||||
AppConfig.canonicalizeMainFrameUrl(
|
AppConfig.canonicalizeMainFrameUrl(
|
||||||
'https://line-two.example/login?shell_app_name=Old'
|
'https://line-two.example/login?shell_app_name=Old'
|
||||||
@@ -78,13 +78,13 @@ void main() {
|
|||||||
|
|
||||||
expect(uri.host, 'line-two.example');
|
expect(uri.host, 'line-two.example');
|
||||||
expect(uri.path, '/login');
|
expect(uri.path, '/login');
|
||||||
expect(uri.queryParameters.containsKey('shell_app_name'), isFalse);
|
expect(uri.queryParameters['shell_app_name'], 'Old');
|
||||||
expect(uri.queryParameters.containsKey('flutter_shell'), isFalse);
|
expect(uri.queryParameters['flutter_shell'], '1');
|
||||||
expect(Uri.splitQueryString(uri.fragment).containsKey('shell_app_logo'),
|
expect(Uri.splitQueryString(uri.fragment).containsKey('shell_app_logo'),
|
||||||
isFalse);
|
isTrue);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('rewrites legacy H5 host main-frame URLs to the canonical host', () {
|
test('does not rewrite H5 main-frame URLs for line switching', () {
|
||||||
final uri = Uri.parse(
|
final uri = Uri.parse(
|
||||||
AppConfig.canonicalizeMainFrameUrl(
|
AppConfig.canonicalizeMainFrameUrl(
|
||||||
'https://h5-test.imharry.work/login?from=runtime'
|
'https://h5-test.imharry.work/login?from=runtime'
|
||||||
@@ -96,8 +96,9 @@ void main() {
|
|||||||
expect(uri.host, 'h5-test.imharry.work');
|
expect(uri.host, 'h5-test.imharry.work');
|
||||||
expect(uri.path, '/login');
|
expect(uri.path, '/login');
|
||||||
expect(uri.queryParameters['from'], 'runtime');
|
expect(uri.queryParameters['from'], 'runtime');
|
||||||
expect(uri.queryParameters.containsKey('shell_app_name'), isFalse);
|
expect(uri.queryParameters['shell_app_name'], 'Old');
|
||||||
expect(Uri.splitQueryString(uri.fragment).containsKey('shell_app_logo'),
|
expect(Uri.splitQueryString(uri.fragment).containsKey('shell_app_logo'),
|
||||||
isFalse);
|
isTrue);
|
||||||
|
expect(AppConfig.shouldRewriteMainFrameUrl(uri.toString()), isFalse);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user