Clock
Введение
Точное моделирование поведения, зависящего от времени, необходимо для проверки корректности приложений. Использование функциональности Clock позволяет разработчикам манипулировать и контролировать время в тестах, обеспечивая точную проверку таких функций, как время рендеринга, тайм-ауты, запланированные задачи без задержек и изменчивости реального времени.
API Clock предоставляет следующие методы для управления временем:
setFixedTime
: Устанавливает фиксированное время дляDate.now()
иnew Date()
.install
: инициализирует часы и позволяет:pauseAt
: Останавливает время в определенный момент.fastForward
: Перематывает время вперед.runFor
: Запускает время на определенную продолжительность.resume
: Возобновляет время.
setSystemTime
: Устанавливает текущее системное время.
Рекомендуемый подход - использовать setFixedTime
для установки времени на конкретное значение. Если это не подходит для вашего случая, вы можете использовать install
, который позволяет позже останавливать время, перематывать его вперед, тикать и т.д. setSystemTime
рекомендуется только для сложных случаев.
page.clock переопределяет нативные глобальные классы и функции, связанные со временем, позволяя им управляться вручную:
Date
setTimeout
clearTimeout
setInterval
clearInterval
requestAnimationFrame
cancelAnimationFrame
requestIdleCallback
cancelIdleCallback
performance
Event.timeStamp
Если вы вызываете install
в любой момент вашего теста, вызов ДОЛЖЕН произойти до любых других вызовов, связанных с часами (см. примечание выше для списка). Вызов этих методов в неправильном порядке приведет к неопределенному поведению. Например, вы не можете вызвать setInterval
, затем install
, а затем clearInterval
, так как install
переопределяет нативное определение функций часов.
Тестирование с предопределенным временем
Часто вам нужно только подделать Date.now
, сохраняя таймеры работающими. Таким образом, время течет естественно, но Date.now
всегда возвращает фиксированное значение.
<div id="current-time" data-testid="current-time"></div>
<script>
const renderTime = () => {
document.getElementById('current-time').textContent =
new Date().toLocaleString();
};
setInterval(renderTime, 1000);
</script>
Последовательное время и таймеры
Иногда ваши таймеры зависят от Date.now
и сбиваются, когда значение Date.now
не меняется со временем. В этом случае вы можете установить часы и перемотать вперед до интересующего времени при тестировании.
<div id="current-time" data-testid="current-time"></div>
<script>
const renderTime = () => {
document.getElementById('current-time').textContent =
new Date().toLocaleString();
};
setInterval(renderTime, 1000);
</script>
- Sync
- Async
# Инициализируйте часы с некоторым временем до тестового времени и позвольте странице загрузиться
# естественно. `Date.now` будет прогрессировать по мере срабатывания таймеров.
page.clock.install(time=datetime.datetime(2024, 2, 2, 8, 0, 0))
page.goto("http://localhost:3333")
# Представьте, что пользователь закрыл крышку ноутбука и открыл ее снова в 10 утра.
# Остановите время, как только оно достигнет этой точки.
page.clock.pause_at(datetime.datetime(2024, 2, 2, 10, 0, 0))
# Проверьте состояние страницы.
expect(page.get_by_test_id("current-time")).to_have_text("2/2/2024, 10:00:00 AM")
# Снова закройте крышку ноутбука и откройте ее в 10:30 утра.
page.clock.fast_forward("30:00")
expect(page.get_by_test_id("current-time")).to_have_text("2/2/2024, 10:30:00 AM")
# Инициализируйте часы с некоторым временем до тестового времени и позвольте странице загрузиться
# естественно. `Date.now` будет прогрессировать по мере срабатывания таймеров.
await page.clock.install(time=datetime.datetime(2024, 2, 2, 8, 0, 0))
await page.goto("http://localhost:3333")
# Представьте, что пользователь закрыл крышку ноутбука и открыл ее снова в 10 утра.
# Остановите время, как только оно достигнет этой точки.
await page.clock.pause_at(datetime.datetime(2024, 2, 2, 10, 0, 0))
# Проверьте состояние страницы.
await expect(page.get_by_test_id("current-time")).to_have_text("2/2/2024, 10:00:00 AM")
# Снова закройте крышку ноутбука и откройте ее в 10:30 утра.
await page.clock.fast_forward("30:00")
await expect(page.get_by_test_id("current-time")).to_have_text("2/2/2024, 10:30:00 AM")
Тестирование мониторинга неактивности
Мониторинг неактивности - это распространенная функция в веб-приложениях, которая выходит из системы пользователей после периода неактивности. Тестирование этой функции может быть сложным, потому что вам нужно долго ждать, чтобы увидеть эффект. С помощью часов вы можете ускорить время и быстро протестировать эту функцию.
<div id="remaining-time" data-testid="remaining-time"></div>
<script>
const endTime = Date.now() + 5 * 60_000;
const renderTime = () => {
const diffInSeconds = Math.round((endTime - Date.now()) / 1000);
if (diffInSeconds <= 0) {
document.getElementById('remaining-time').textContent =
'Вы были выведены из системы из-за неактивности.';
} else {
document.getElementById('remaining-time').textContent =
`Вы будете выведены из системы через ${diffInSeconds} секунд.`;
}
setTimeout(renderTime, 1000);
};
renderTime();
</script>
<button type="button">Взаимодействие</button>
- Sync
- Async
# Начальное время не имеет значения для теста, поэтому мы можем выбрать текущее время.
page.clock.install()
page.goto("http://localhost:3333")
# Взаимодействуйте со страницей
page.get_by_role("button").click()
# Перемотайте время на 5 минут вперед, как если бы пользователь ничего не делал.
# Перемотка вперед похожа на закрытие крышки ноутбука и открытие ее через 5 минут.
# Все таймеры, которые должны были сработать, сработают сразу, как в реальном браузере.
page.clock.fast_forward("05:00")
# Проверьте, что пользователь был автоматически выведен из системы.
expect(page.get_by_text("Вы были выведены из системы из-за неактивности.")).to_be_visible()
# Начальное время не имеет значения для теста, поэтому мы можем выбрать текущее время.
await page.clock.install()
await page.goto("http://localhost:3333")
# Взаимодействуйте со страницей
await page.get_by_role("button").click()
# Перемотайте время на 5 минут вперед, как если бы пользователь ничего не делал.
# Перемотка вперед похожа на закрытие крышки ноутбука и открытие ее через 5 минут.
# Все таймеры, которые должны были сработать, сработают сразу, как в реальном браузере.
await page.clock.fast_forward("05:00")
# Проверьте, что пользователь был автоматически выведен из системы.
await expect(page.getByText("Вы были выведены из системы из-за неактивности.")).toBeVisible()
Ручное перемещение по времени с последовательным срабатыванием всех таймеров
В редких случаях вы можете захотеть вручную перемещаться по времени, срабатывая все таймеры и анимационные кадры в процессе, чтобы достичь тонкого контроля над течением времени.
<div id="current-time" data-testid="current-time"></div>
<script>
const renderTime = () => {
document.getElementById('current-time').textContent =
new Date().toLocaleString();
};
setInterval(renderTime, 1000);
</script>
- Sync
- Async
# Инициализируйте часы с конкретным временем, позвольте странице загрузиться естественно.
page.clock.install(
time=datetime.datetime(2024, 2, 2, 8, 0, 0, tzinfo=datetime.timezone.pst),
)
page.goto("http://localhost:3333")
locator = page.get_by_test_id("current-time")
# Остановите поток времени, остановите таймеры, теперь у вас есть ручной контроль
# над временем страницы.
page.clock.pause_at(datetime.datetime(2024, 2, 2, 10, 0, 0))
expect(locator).to_have_text("2/2/2024, 10:00:00 AM")
# Ручное перемещение по времени, срабатывая все таймеры в процессе.
# В этом случае время будет обновлено на экране 2 раза.
page.clock.run_for(2000)
expect(locator).to_have_text("2/2/2024, 10:00:02 AM")
# Инициализируйте часы с конкретным временем, позвольте странице загрузиться естественно.
await page.clock.install(time=
datetime.datetime(2024, 2, 2, 8, 0, 0, tzinfo=datetime.timezone.pst),
)
await page.goto("http://localhost:3333")
locator = page.get_by_test_id("current-time")
# Остановите поток времени, остановите таймеры, теперь у вас есть ручной контроль
# над временем страницы.
await page.clock.pause_at(datetime.datetime(2024, 2, 2, 10, 0, 0))
await expect(locator).to_have_text("2/2/2024, 10:00:00 AM")
# Ручное перемещение по времени, срабатывая все таймеры в процессе.
# В этом случае время будет обновлено на экране 2 раза.
await page.clock.run_for(2000)
await expect(locator).to_have_text("2/2/2024, 10:00:02 AM")