Universal Router предоставляет возможность создавать маршруты,
которые возвращают результаты асинхронно. Это особенно важно для
современных приложений, где загрузка данных с сервера или динамическая
генерация компонентов требует использования промисов или
async/await. Асинхронный маршрут определяется через функцию
action, которая может быть async или
возвращать промис.
const router = new UniversalRouter([
{
path: '/user/:id',
async action(context) {
const response = await fetch(`/api/users/${context.params.id}`);
const user = await response.json();
return user;
}
}
]);
В этом примере action получает объект
context, содержащий параметры маршрута, и возвращает данные
пользователя после завершения асинхронной операции.
Объект context в Universal Router включает ключевые
свойства, которые необходимы для работы с асинхронными маршрутами:
:id.Асинхронная функция action может использовать все эти
свойства для получения данных, логирования или перенаправлений.
Тестирование асинхронных маршрутов требует учета нескольких особенностей:
async/await Каждый маршрут,
возвращающий промис, должен тестироваться с использованием асинхронного
кода. В Jest или Mocha тест может выглядеть так:test('маршрут /user/:id возвращает пользователя', async () => {
const router = new UniversalRouter([
{
path: '/user/:id',
async action({ params }) {
return { id: params.id, name: 'Alice' };
}
}
]);
const result = await router.resolve({ pathname: '/user/42' });
expect(result).toEqual({ id: '42', name: 'Alice' });
});
test('маршрут выбрасывает ошибку при недоступном пользователе', async () => {
const router = new UniversalRouter([
{
path: '/user/:id',
async action({ params }) {
if (params.id !== '1') throw new Error('User not found');
return { id: '1', name: 'Alice' };
}
}
]);
await expect(router.resolve({ pathname: '/user/42' }))
.rejects
.toThrow('User not found');
});
global.fetch = jest.fn(() =>
Promise.resolve({
json: () => Promise.resolve({ id: '42', name: 'Alice' })
})
);
test('маршрут /user/:id возвращает данные из fetch', async () => {
const router = new UniversalRouter([
{
path: '/user/:id',
async action({ params }) {
const response = await fetch(`/api/users/${params.id}`);
return await response.json();
}
}
]);
const result = await router.resolve({ pathname: '/user/42' });
expect(result).toEqual({ id: '42', name: 'Alice' });
expect(fetch).toHaveBeenCalledWith('/api/users/42');
});
Universal Router поддерживает вложенные маршруты, где каждый уровень может быть асинхронным:
const router = new UniversalRouter([
{
path: '/user/:id',
async action({ params, next }) {
const user = await fetchUser(params.id);
const details = await next();
return { user, details };
},
children: [
{
path: '/posts',
async action({ params }) {
return fetchPosts(params.id);
}
}
]
}
]);
Тестирование таких маршрутов требует вызова resolve с
полным путем и проверки объединенного результата:
const result = await router.resolve({ pathname: '/user/42/posts' });
expect(result).toEqual({
user: { id: '42', name: 'Alice' },
details: [{ id: 1, title: 'Post 1' }]
});
Асинхронные маршруты могут возвращать объект с ключом
redirect, чтобы инициировать перенаправление:
async action({ params }) {
if (!params.id) return { redirect: '/login' };
return { id: params.id, name: 'Alice' };
}
Тестирование редиректов включает проверку возвращаемого объекта:
const result = await router.resolve({ pathname: '/user/' });
expect(result).toEqual({ redirect: '/login' });
await при вызове
router.resolve, иначе тесты не дождутся промисов.Асинхронные маршруты делают Universal Router гибким инструментом, но требуют внимательного подхода при тестировании, особенно при взаимодействии с внешними API или при динамическом формировании данных.