Часть сценариев зависит от времени, и реальное время делает их либо медленными, либо недетерминированными. Сессия истекает через тридцать минут, баннер появляется через сутки, поллинг обновляет данные каждые десять секунд, скидка действует до определённой даты. Ждать этого по-настоящему невозможно, а угадывать паузами - значит вернуться к флаку из главы про ожидания. Playwright решает это иначе: page.clock даёт тесту управлять временем страницы напрямую.
Механизм подменяет часы и таймеры страницы на контролируемые. clock.install ставит эту подмену - и делают это до навигации, до page.goto, чтобы приложение с самого старта видело управляемое время, а не настоящее. С этого момента Date, setTimeout, setInterval внутри страницы подчиняются тесту: время перестаёт течь само по себе и двигается только тогда и настолько, насколько скажет сценарий.
Для сценариев, зависящих от конкретной даты, есть фиксация времени. setFixedTime заставляет страницу считать, что сейчас именно этот момент, - и это делает воспроизводимыми тесты про "действует до 31 декабря", "показать поздравление в новогоднюю ночь", "просрочено вчера". Вместо того чтобы зависеть от того, когда прогон запущен, тест сам задаёт дату, и его результат одинаков в любой день и в любом часовом поясе CI.
Для таймеров и интервалов есть прокрутка времени. fastForward мгновенно перематывает часы вперёд на заданный интервал - и то, что должно было случиться через тридцать минут (истечение сессии) или через десять секунд (тик поллинга), происходит сразу, без реального ожидания. runFor прокручивает время шагами, давая сработать промежуточным таймерам. Так тест проверяет поведение по времени за миллисекунды, а не за минуты.
Полезно один раз увидеть оба режима вместе - фиксацию даты и прокрутку таймера. Ниже - установка часов до навигации, фиксированная дата для сценария и перемотка на тридцать минут, проверяющая истечение сессии. К этой форме возвращаются, когда в сценарии всплывает зависимость от времени: сессия, срок, поллинг, отложенный баннер, - всё, что раньше пытались поймать реальным ожиданием.
Прямая польза - в закрытии строки "Время/TZ" из главы про flaky. Тесты, которые падали ночью, в конце месяца или в CI с другим часовым поясом, чинятся не подгонкой среды, а фиксацией времени: сценарий сам задаёт момент и не зависит от того, когда и где его запустили. Недетерминизм по времени превращается из источника случайных падений в управляемый вход теста, такой же, как данные.
Важно ставить часы до того, как приложение их прочитает. Если clock.install вызвать после page.goto, приложение успеет получить настоящее время при старте, и подмена подхватит лишь то, что произойдёт позже, - а часть логики уже отработает на реальных часах. Поэтому управление временем настраивают в начале сценария, до навигации, чтобы страница с первого кадра жила в контролируемом времени, а не переключалась на него на полпути.
Типичные провалы работы со временем предсказуемы. Ждать реальные минуты, пока истечёт сессия или сработает поллинг, - медленный и всё равно нестабильный тест. Зависеть от системной даты вместо setFixedTime - падения в конце месяца и в чужом часовом поясе. И вызвать install слишком поздно, после навигации, - часть логики на настоящих часах и незакрытая гонка. Ставьте часы до goto, фиксируйте дату для датозависимых сценариев и прокручивайте таймеры вместо реального ожидания.
// Часы ставят ДО навигации, чтобы приложение сразу жило в контролируемом времени
await page.clock.install({ time: new Date('2026-07-26T10:00:00') })
await page.goto('/dashboard')
// Фиксированная дата - воспроизводимый сценарий про срок
await page.clock.setFixedTime(new Date('2026-12-31T23:59:00'))
// Перемотка на 30 минут: сессия истекает без реального ожидания
await page.clock.fastForward('30:00')
await expect(page.getByText('Сессия истекла')).toBeVisible()