Universal Router — это гибкая библиотека маршрутизации для JavaScript, которая позволяет управлять переходами между страницами или состояниями приложения на клиентской и серверной стороне. При написании E2E тестов правильная организация маршрутов позволяет полностью контролировать сценарии пользователя и проверять их поведение без зависимости от внутренней логики приложения.
E2E тестирование с Universal Router строится вокруг двух ключевых
принципов: точное определение маршрутов и
контроль навигации в тестовой среде. Каждый маршрут
определяется объектом с полями path, action, и
опционально children. В тестах важно учитывать, что
маршруты должны быть детерминированными, чтобы тесты оставались
стабильными при изменениях приложения.
import UniversalRouter from 'universal-router';
const routes = [
{ path: '/', action: () => 'Главная' },
{ path: '/login', action: () => 'Страница входа' },
{ path: '/profile/:id', action: ({ params }) => `Профиль пользователя ${params.id}` },
];
const router = new UniversalRouter(routes);
При E2E тестировании маршруты можно вызывать напрямую или через симуляцию навигации в браузере, используя инструменты вроде Cypress или Playwright. Прямой вызов маршрута позволяет проверять корректность сопоставления URL и действия, не затрагивая DOM. Например:
const result = await router.resolve({ pathname: '/profile/42' });
console.assert(result === 'Профиль пользователя 42');
Для эффективного тестирования рекомендуется выделять тестовые маршруты, которые могут использовать фиктивные данные, мок-сервисы или специальные компоненты. Это позволяет изолировать тесты от реальной API и ускоряет их выполнение.
const testRoutes = [
{
path: '/dashboard',
action: () => ({ title: 'Тестовая панель', data: [] })
},
{
path: '/settings',
action: () => ({ title: 'Настройки', editable: true })
}
];
const testRouter = new UniversalRouter(testRoutes);
Такой подход позволяет создавать контролируемую среду, где каждый маршрут возвращает предсказуемый результат, что критически важно для стабильности E2E тестов.
Universal Router поддерживает асинхронные action
функции, что позволяет моделировать реальные сценарии загрузки данных. В
E2E тестах это особенно важно при проверке поведения приложения при
задержках сети или при ошибках API.
const asyncRoutes = [
{
path: '/products/:id',
action: async ({ params }) => {
const response = await fetch(`/api/products/${params.id}`);
const data = await response.json();
return data.name;
}
}
];
const asyncRouter = new UniversalRouter(asyncRoutes);
Тесты должны использовать мокирование сетевых запросов или встроенные промисы, чтобы контролировать результаты и проверять правильность обработки асинхронных данных:
const mockFetch = jest.fn(() =>
Promise.resolve({ json: () => Promise.resolve({ name: 'Товар 1' }) })
);
global.fetch = mockFetch;
const result = await asyncRouter.resolve({ pathname: '/products/1' });
console.assert(result === 'Товар 1');
Параметры, передаваемые через URL, играют ключевую роль в тестах.
Universal Router автоматически извлекает их в объект
params, что позволяет писать тесты, проверяющие
динамическую генерацию контента:
const userRoutes = [
{ path: '/user/:userId', action: ({ params }) => `Пользователь ${params.userId}` }
];
const userRouter = new UniversalRouter(userRoutes);
const output = await userRouter.resolve({ pathname: '/user/99' });
console.assert(output === 'Пользователь 99');
В E2E тестах рекомендуется проверять как валидные, так и невалидные параметры, чтобы убедиться, что маршрутизация корректно обрабатывает ошибки:
try {
await userRouter.resolve({ pathname: '/user/' });
} catch (err) {
console.assert(err.message.includes('No route matched'));
}
Universal Router позволяет строить вложенные маршруты через поле
children. Для E2E тестов это означает необходимость
проверки последовательной навигации:
const nestedRoutes = [
{
path: '/admin',
action: () => 'Админ панель',
children: [
{ path: '/users', action: () => 'Список пользователей' },
{ path: '/settings', action: () => 'Настройки админа' }
]
}
];
const nestedRouter = new UniversalRouter(nestedRoutes);
const usersPage = await nestedRouter.resolve({ pathname: '/admin/users' });
console.assert(usersPage === 'Список пользователей');
В тестах важно проверять корректность цепочки маршрутов и передачу параметров на всех уровнях вложенности, чтобы не допустить ошибок при глубокой навигации.
Для тестов, которые проверяют работу приложения в реальном браузере,
Universal Router может быть интегрирован с History API или Hash-based
навигацией. E2E инструменты, такие как Playwright или Cypress, позволяют
симулировать клики по ссылкам и проверять вызовы
router.resolve:
await page.goto('/login');
const content = await page.textContent('h1');
console.assert(content === 'Страница входа');
Дополнительно можно тестировать редиректы и fallback маршруты, что критично для проверки пользовательского опыта при неправильных URL.
const fallbackRoutes = [
{ path: '/', action: () => 'Главная' },
{ path: '(.*)', action: () => '404 Страница не найдена' }
];
const fallbackRouter = new UniversalRouter(fallbackRoutes);
const notFound = await fallbackRouter.resolve({ pathname: '/unknown' });
console.assert(notFound === '404 Страница не найдена');
Universal Router предоставляет возможность логировать вызовы маршрутов через middleware-подобные обертки. Это помогает анализировать поведение приложения в E2E тестах:
async function loggingResolver(context, next) {
console.log('Переход к', context.pathname);
const result = await next();
console.log('Результат:', result);
return result;
}
const loggedRouter = new UniversalRouter(routes, { resolveRoute: loggingResolver });
Такой подход позволяет отслеживать весь путь навигации и облегчает диагностику ошибок в сложных сценариях.
Для изоляции тестов можно создавать специальные маршруты с моковыми действиями. Это позволяет проверить интерфейс и навигацию без зависимости от реальной базы данных или внешних API:
const mockRoutes = [
{ path: '/mock-data', action: () => ({ items: [1, 2, 3] }) }
];
const mockRouter = new UniversalRouter(mockRoutes);
const data = await mockRouter.resolve({ pathname: '/mock-data' });
console.assert(data.items.length === 3);
Использование таких маршрутов ускоряет тесты и делает их предсказуемыми.