diff --git a/packages/go_router/lib/src/configuration.dart b/packages/go_router/lib/src/configuration.dart index ef408b442708..0cc36ee5b85c 100644 --- a/packages/go_router/lib/src/configuration.dart +++ b/packages/go_router/lib/src/configuration.dart @@ -260,12 +260,17 @@ class RouteConfiguration { /// Normalizes a URI by ensuring it has a valid path and removing trailing slashes. static Uri normalizeUri(Uri uri) { - if (uri.hasEmptyPath) { - return uri.replace(path: '/'); - } else if (uri.path.length > 1 && uri.path.endsWith('/')) { - return uri.replace(path: uri.path.substring(0, uri.path.length - 1)); + String path = uri.path; + if (!path.startsWith('/')) { + path = '/$path'; } - return uri; + if (path.length > 1 && path.endsWith('/')) { + path = path.substring(0, path.length - 1); + } + if (path == uri.path) { + return uri; + } + return uri.replace(path: path); } /// The global key for top level navigator. diff --git a/packages/go_router/pending_changelogs/fix_assertion_missing_slash.yaml b/packages/go_router/pending_changelogs/fix_assertion_missing_slash.yaml new file mode 100644 index 000000000000..378fbfff1ce0 --- /dev/null +++ b/packages/go_router/pending_changelogs/fix_assertion_missing_slash.yaml @@ -0,0 +1,3 @@ +changelog: | + - Fixes an assertion failure when navigating to URLs with hash fragments missing a leading slash. +version: patch diff --git a/packages/go_router/test/configuration_test.dart b/packages/go_router/test/configuration_test.dart index 88185f026c12..2e6e9cc39321 100644 --- a/packages/go_router/test/configuration_test.dart +++ b/packages/go_router/test/configuration_test.dart @@ -1010,6 +1010,54 @@ void main() { ); }, ); + + group('normalizeUri', () { + test('adds leading slash if missing', () { + expect(RouteConfiguration.normalizeUri(Uri.parse('foo')).path, '/foo'); + }); + + test('handles empty path', () { + expect(RouteConfiguration.normalizeUri(Uri.parse('')).path, '/'); + }); + + test('removes trailing slash if length > 1', () { + expect( + RouteConfiguration.normalizeUri(Uri.parse('/foo/')).path, + '/foo', + ); + }); + + test('does not remove slash for root root', () { + expect(RouteConfiguration.normalizeUri(Uri.parse('/')).path, '/'); + }); + + test('preserves query parameters and fragments', () { + final Uri uri = RouteConfiguration.normalizeUri(Uri.parse('foo?a=b#c')); + expect(uri.path, '/foo'); + expect(uri.queryParameters['a'], 'b'); + expect(uri.fragment, 'c'); + }); + + test('handles hash fragments with authority', () { + final Uri uri = RouteConfiguration.normalizeUri( + Uri.parse('http://localhost:3000/#foo'), + ); + expect(uri.path, '/'); + expect(uri.fragment, 'foo'); + }); + + test('handles hash fragments without authority', () { + final Uri uri = RouteConfiguration.normalizeUri(Uri.parse('/#foo')); + expect(uri.path, '/'); + expect(uri.fragment, 'foo'); + }); + + test('returns same instance if already normalized', () { + final Uri uri = Uri.parse('/foo'); + final Uri normalized = RouteConfiguration.normalizeUri(uri); + expect(identical(uri, normalized), isTrue); + }); + }); }); } diff --git a/packages/go_router/test/parser_test.dart b/packages/go_router/test/parser_test.dart index 3859f272f8f5..c55e84230892 100644 --- a/packages/go_router/test/parser_test.dart +++ b/packages/go_router/test/parser_test.dart @@ -662,4 +662,33 @@ void main() { expect(match.matches, hasLength(1)); expect(matchesObj.error, isNull); }); + + testWidgets( + 'GoRouteInformationParser can handle path without leading slash', + (WidgetTester tester) async { + final routes = [ + GoRoute( + path: '/abc', + builder: (_, __) => const Placeholder(), + ), + ]; + final GoRouteInformationParser parser = await createParser( + tester, + routes: routes, + redirectLimit: 100, + redirect: (_, __) => null, + ); + + final BuildContext context = tester.element(find.byType(Router)); + + final RouteMatchList matchesObj = await parser + .parseRouteInformationWithDependencies( + createRouteInformation('abc'), + context, + ); + final List matches = matchesObj.matches; + expect(matches.length, 1); + expect(matchesObj.uri.toString(), '/abc'); + }, + ); }