| // Copyright 2013 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import 'package:flutter/material.dart'; |
| import 'package:flutter_test/flutter_test.dart'; |
| import 'package:go_router/src/configuration.dart'; |
| import 'package:go_router/src/match.dart'; |
| import 'package:go_router/src/matching.dart'; |
| import 'package:go_router/src/parser.dart'; |
| |
| void main() { |
| test('GoRouteInformationParser can parse route', () async { |
| final List<GoRoute> routes = <GoRoute>[ |
| GoRoute( |
| path: '/', |
| builder: (_, __) => const Placeholder(), |
| routes: <GoRoute>[ |
| GoRoute( |
| path: 'abc', |
| builder: (_, __) => const Placeholder(), |
| ), |
| ], |
| ), |
| ]; |
| final GoRouteInformationParser parser = GoRouteInformationParser( |
| configuration: RouteConfiguration( |
| routes: routes, |
| redirectLimit: 100, |
| topRedirect: (_) => null, |
| ), |
| ); |
| |
| RouteMatchList matchesObj = await parser |
| .parseRouteInformation(const RouteInformation(location: '/')); |
| List<RouteMatch> matches = matchesObj.matches; |
| expect(matches.length, 1); |
| expect(matches[0].queryParams.isEmpty, isTrue); |
| expect(matches[0].extra, isNull); |
| expect(matches[0].fullUriString, '/'); |
| expect(matches[0].subloc, '/'); |
| expect(matches[0].route, routes[0]); |
| |
| final Object extra = Object(); |
| matchesObj = await parser.parseRouteInformation( |
| RouteInformation(location: '/abc?def=ghi', state: extra)); |
| matches = matchesObj.matches; |
| expect(matches.length, 2); |
| expect(matches[0].queryParams.length, 1); |
| expect(matches[0].queryParams['def'], 'ghi'); |
| expect(matches[0].extra, extra); |
| expect(matches[0].fullUriString, '/?def=ghi'); |
| expect(matches[0].subloc, '/'); |
| expect(matches[0].route, routes[0]); |
| |
| expect(matches[1].queryParams.length, 1); |
| expect(matches[1].queryParams['def'], 'ghi'); |
| expect(matches[1].extra, extra); |
| expect(matches[1].fullUriString, '/abc?def=ghi'); |
| expect(matches[1].subloc, '/abc'); |
| expect(matches[1].route, routes[0].routes[0]); |
| }); |
| |
| test('GoRouteInformationParser can retrieve route by name', () async { |
| final List<GoRoute> routes = <GoRoute>[ |
| GoRoute( |
| path: '/', |
| builder: (_, __) => const Placeholder(), |
| routes: <GoRoute>[ |
| GoRoute( |
| path: 'abc', |
| name: 'lowercase', |
| builder: (_, __) => const Placeholder(), |
| ), |
| GoRoute( |
| path: 'efg', |
| name: 'camelCase', |
| builder: (_, __) => const Placeholder(), |
| ), |
| GoRoute( |
| path: 'hij', |
| name: 'snake_case', |
| builder: (_, __) => const Placeholder(), |
| ), |
| ], |
| ), |
| ]; |
| |
| final RouteConfiguration configuration = RouteConfiguration( |
| routes: routes, |
| redirectLimit: 100, |
| topRedirect: (_) => null, |
| ); |
| |
| expect(configuration.namedLocation('lowercase'), '/abc'); |
| expect(configuration.namedLocation('LOWERCASE'), '/abc'); |
| expect(configuration.namedLocation('camelCase'), '/efg'); |
| expect(configuration.namedLocation('camelcase'), '/efg'); |
| expect(configuration.namedLocation('snake_case'), '/hij'); |
| expect(configuration.namedLocation('SNAKE_CASE'), '/hij'); |
| |
| // With query parameters |
| expect(configuration.namedLocation('lowercase'), '/abc'); |
| expect( |
| configuration.namedLocation('lowercase', |
| queryParams: const <String, String>{'q': '1'}), |
| '/abc?q=1'); |
| expect( |
| configuration.namedLocation('lowercase', |
| queryParams: const <String, String>{'q': '1', 'g': '2'}), |
| '/abc?q=1&g=2'); |
| }); |
| |
| test('GoRouteInformationParser returns error when unknown route', () async { |
| final List<GoRoute> routes = <GoRoute>[ |
| GoRoute( |
| path: '/', |
| builder: (_, __) => const Placeholder(), |
| routes: <GoRoute>[ |
| GoRoute( |
| path: 'abc', |
| builder: (_, __) => const Placeholder(), |
| ), |
| ], |
| ), |
| ]; |
| final GoRouteInformationParser parser = GoRouteInformationParser( |
| configuration: RouteConfiguration( |
| routes: routes, |
| redirectLimit: 100, |
| topRedirect: (_) => null, |
| ), |
| ); |
| |
| final RouteMatchList matchesObj = await parser |
| .parseRouteInformation(const RouteInformation(location: '/def')); |
| final List<RouteMatch> matches = matchesObj.matches; |
| expect(matches.length, 1); |
| expect(matches[0].queryParams.isEmpty, isTrue); |
| expect(matches[0].extra, isNull); |
| expect(matches[0].fullUriString, '/def'); |
| expect(matches[0].subloc, '/def'); |
| expect(matches[0].error!.toString(), |
| 'Exception: no routes for location: /def'); |
| }); |
| |
| test('GoRouteInformationParser can work with route parameters', () async { |
| final List<GoRoute> routes = <GoRoute>[ |
| GoRoute( |
| path: '/', |
| builder: (_, __) => const Placeholder(), |
| routes: <GoRoute>[ |
| GoRoute( |
| path: ':uid/family/:fid', |
| builder: (_, __) => const Placeholder(), |
| ), |
| ], |
| ), |
| ]; |
| final GoRouteInformationParser parser = GoRouteInformationParser( |
| configuration: RouteConfiguration( |
| routes: routes, |
| redirectLimit: 100, |
| topRedirect: (_) => null, |
| ), |
| ); |
| |
| final RouteMatchList matchesObj = await parser.parseRouteInformation( |
| const RouteInformation(location: '/123/family/456')); |
| final List<RouteMatch> matches = matchesObj.matches; |
| |
| expect(matches.length, 2); |
| expect(matches[0].queryParams.isEmpty, isTrue); |
| expect(matches[0].extra, isNull); |
| expect(matches[0].fullUriString, '/'); |
| expect(matches[0].subloc, '/'); |
| |
| expect(matches[1].queryParams.isEmpty, isTrue); |
| expect(matches[1].extra, isNull); |
| expect(matches[1].fullUriString, '/123/family/456'); |
| expect(matches[1].subloc, '/123/family/456'); |
| expect(matches[1].encodedParams.length, 2); |
| expect(matches[1].encodedParams['uid'], '123'); |
| expect(matches[1].encodedParams['fid'], '456'); |
| }); |
| |
| test( |
| 'GoRouteInformationParser processes top level redirect when there is no match', |
| () async { |
| final List<GoRoute> routes = <GoRoute>[ |
| GoRoute( |
| path: '/', |
| builder: (_, __) => const Placeholder(), |
| routes: <GoRoute>[ |
| GoRoute( |
| path: ':uid/family/:fid', |
| builder: (_, __) => const Placeholder(), |
| ), |
| ], |
| ), |
| ]; |
| final GoRouteInformationParser parser = GoRouteInformationParser( |
| configuration: RouteConfiguration( |
| routes: routes, |
| redirectLimit: 100, |
| topRedirect: (GoRouterState state) { |
| if (state.location != '/123/family/345') { |
| return '/123/family/345'; |
| } |
| return null; |
| }, |
| ), |
| ); |
| |
| final RouteMatchList matchesObj = await parser |
| .parseRouteInformation(const RouteInformation(location: '/random/uri')); |
| final List<RouteMatch> matches = matchesObj.matches; |
| |
| expect(matches.length, 2); |
| expect(matches[0].fullUriString, '/'); |
| expect(matches[0].subloc, '/'); |
| |
| expect(matches[1].fullUriString, '/123/family/345'); |
| expect(matches[1].subloc, '/123/family/345'); |
| }); |
| |
| test( |
| 'GoRouteInformationParser can do route level redirect when there is a match', |
| () async { |
| final List<GoRoute> routes = <GoRoute>[ |
| GoRoute( |
| path: '/', |
| builder: (_, __) => const Placeholder(), |
| routes: <GoRoute>[ |
| GoRoute( |
| path: ':uid/family/:fid', |
| builder: (_, __) => const Placeholder(), |
| ), |
| GoRoute( |
| path: 'redirect', |
| redirect: (_) => '/123/family/345', |
| builder: (_, __) => throw UnimplementedError(), |
| ), |
| ], |
| ), |
| ]; |
| final GoRouteInformationParser parser = GoRouteInformationParser( |
| configuration: RouteConfiguration( |
| routes: routes, |
| redirectLimit: 100, |
| topRedirect: (_) => null, |
| ), |
| ); |
| |
| final RouteMatchList matchesObj = await parser |
| .parseRouteInformation(const RouteInformation(location: '/redirect')); |
| final List<RouteMatch> matches = matchesObj.matches; |
| |
| expect(matches.length, 2); |
| expect(matches[0].fullUriString, '/'); |
| expect(matches[0].subloc, '/'); |
| |
| expect(matches[1].fullUriString, '/123/family/345'); |
| expect(matches[1].subloc, '/123/family/345'); |
| }); |
| |
| test('GoRouteInformationParser throws an exception when route is malformed', |
| () async { |
| final List<GoRoute> routes = <GoRoute>[ |
| GoRoute( |
| path: '/abc', |
| builder: (_, __) => const Placeholder(), |
| ), |
| ]; |
| final GoRouteInformationParser parser = GoRouteInformationParser( |
| configuration: RouteConfiguration( |
| routes: routes, |
| redirectLimit: 100, |
| topRedirect: (_) => null, |
| ), |
| ); |
| |
| expect(() async { |
| await parser.parseRouteInformation( |
| const RouteInformation(location: '::Not valid URI::')); |
| }, throwsA(isA<FormatException>())); |
| }); |
| |
| test('GoRouteInformationParser returns an error if a redirect is detected.', |
| () async { |
| final List<GoRoute> routes = <GoRoute>[ |
| GoRoute( |
| path: '/abc', |
| builder: (_, __) => const Placeholder(), |
| redirect: (GoRouterState state) => state.location, |
| ), |
| ]; |
| final GoRouteInformationParser parser = GoRouteInformationParser( |
| configuration: RouteConfiguration( |
| routes: routes, |
| redirectLimit: 5, |
| topRedirect: (_) => null, |
| ), |
| ); |
| |
| final RouteMatchList matchesObj = await parser |
| .parseRouteInformation(const RouteInformation(location: '/abd')); |
| final List<RouteMatch> matches = matchesObj.matches; |
| |
| expect(matches, hasLength(1)); |
| expect(matches.first.error, isNotNull); |
| }); |
| } |