Блог и новости

Мониторинг сторонних ресурсов с Playwright: Как избежать «смерти от Ad Blocker» и блокировки рендеринга

Проверки Блог
Playwright и Pingera логотипы на фоне js кода
Современные веб-сайты — это сложная мозаика из собственных и сторонних ресурсов. Шрифты, оптимизированные медиафайлы, скрипты аналитики и рекламы — большинство сайтов включают внешние зависимости, которые не находятся под их прямым контролем.
И хотя с точки зрения производительности всегда лучше размещать ресурсы самостоятельно, избежать внешних файлов часто невозможно. Но будьте бдительны: использование стороннего кода может привести к серьезным проблемам.
Внешние ресурсы нередко интегрированы слишком тесно или некорректно. Например, блокировщики рекламы могут заблокировать критичный скрипт, или у внешнего поставщика произойдет сбой. Оба сценария могут вызвать простой фронтенда, ухудшить пользовательский опыт (UX) или полностью сломать важный функционал.
Как технический руководитель в сфере мониторинга, я считаю, что критически важно не просто тестировать, а мониторить отказы внешних ресурсов.
Давайте рассмотрим примеры того, как сторонний код может навредить, и разберем, как использовать Playwright для мониторинга вашего стека, чтобы внешние провайдеры или блокировщики не создавали проблем для вашего бизнеса. 🫡

Паттерны, которые ломают сайты из-за внешних ресурсов

Основываясь на моем опыте, можно выделить два основных сценария, создающих наибольшие трудности.

1. Медленные и блокирующие рендеринг ресурсы

Много лет назад я работал в e-commerce, и мы использовали внешнего провайдера для рекламных баннеров. Это было быстрое решение для сбора статистики кликов. Баннеры внедрялись с помощью синхронных <script> элементов в верхней части страницы (above the fold):
<script type="text/javascript" src="http://some-provider.js"></script>
Синхронный скрипт останавливает парсер HTML и заставляет браузер ждать его загрузки и выполнения. Рендеринг возобновляется только после завершения работы скрипта.

Проблема возникла, когда внешний поставщик столкнулся с сбоем: запросы к его скрипту не завершались ошибкой, а просто зависали и в итоге таймаутили. В результате браузер начинал рендерить страницу, доходил до слайд-шоу, останавливался и ждал 10-20 секунд, пока скрипт не вернет ошибку по таймауту. В это время наши клиенты видели белый экран. Фактически, внешний поставщик обрушил фронтенд из-за некорректной реализации синхронного скрипта.

Вывод: Каждая внешняя зависимость — это риск. Избегайте критических, блокирующих рендеринг элементов. Проверяйте, что произойдет, если эти ресурсы перестанут работать или сильно замедлятся.

2. Тесно связанные JavaScript-зависимости

Многие разработчики сталкиваются с тем, что их критически важные функции (например, оформление заказа) ломаются из-за выброшенного JavaScript-исключения. Это часто происходит из-за блокировщиков рекламы.
Блокировщик выполняет свою работу — блокирует трекеры (внешние скрипты). Но проблема в том, что ваш собственный код может ожидать наличия этих скриптов. Например, если ваш JavaScript полагается на доступность функции sendTracking в глобальном объекте window, блокировщик, который ее удаляет или не дает загрузить, может спровоцировать сбой.
Вот простой пример:
// tracking-script.js:
window.trackThing = function() { /* report data */ }

// /script/index.js:
function init() {
  // ЭТО вызовет исключение, если `tracking-script.js` был заблокирован!
  trackThing("page loaded or something"); 
}
init();
Если tracking-script.js заблокирован, ваш код выбрасывает исключение и может "уронить" все приложение. При внедрении внешнего JavaScript-функционала всегда необходимо убедиться, что система остается работоспособной даже в случае ошибок. Обработка заказа важнее, чем его отслеживание.

Как тестировать и мониторить сторонние ресурсы с Playwright

Playwright — идеальный инструмент для имитации сетевых условий и проверки устойчивости вашего приложения. В Pingera мы используем его для синтетических проверок, которые позволяют проактивно выявлять такие проблемы.

Эмуляция медленных сторонних ресурсов

Используя метод page.route, мы можем перехватить запросы и искусственно замедлить их. Это позволяет оценить влияние внешних задержек на метрики производительности, такие как Largest Contentful Paint (LCP).
Сценарий Playwright:
Мы создаем простой тест, который замедляет все запросы к внешним ресурсам на 10 секунд.
const { test, expect } = require("@playwright/test")

test("тест с медленными ресурсами", async ({ page }) => {
  // Перехватываем все запросы
  page.route(
    "**",
    (route) =>
      new Promise((resolve) => {
        const requestURL = route.request().url()
        // Разрешаем запросы к собственному домену мгновенно
        if (requestURL.match(/http:\/\/localhost:8080/)) {
          resolve(route.continue())
        } else {
          // Задерживаем внешние запросы на 10 секунд
          setTimeout(() => {
            console.log(`Задержка: ${requestURL}`)
            resolve(route.continue())
          }, 10000)
        }
      })
  )

  await page.goto("http://localhost:8080")

  // Оценка LCP (Largest Contentful Paint)
  const largestContentfulPaint = await page.evaluate(() => {
    return new Promise((resolve) => {
      new PerformanceObserver((l) => {
        const entries = l.getEntries()
        const largestPaintEntry = entries.at(-1)
        resolve(largestPaintEntry.startTime)
      }).observe({
        type: "largest-contentful-paint",
        buffered: true,
      })
    })
  })
  
  console.log(`LCP: ${parseFloat(largestContentfulPaint)}мс`)
  await expect(page).toHaveTitle(/Frontend downtime/)
})
Результат:
  • Время выполнения теста и LCP увеличиваются до ∼10 секунд.
  • Мы видим, что внешний ресурс, даже если он размещен внизу страницы, может сильно замедлить событие onload, на котором Playwright по умолчанию завершает page.goto, и что более важно — заблокировать рендеринг страницы для пользователя на 10 секунд.

Блокировка внешних ресурсов и отслеживание ошибок

Чтобы имитировать поведение Ad Blocker, мы можем полностью блокировать запросы к внешним доменам с помощью page.route и метода route.abort(). При этом мы мониторим ошибки страницы с помощью события page.on("pageerror").
test("тест с заблокированными сторонними ресурсами", async ({ page }) => {
  page.route("**", (route) => {
    const requestURL = route.request().url()
    // Разрешаем запросы к собственному домену
    if (requestURL.match(/http:\/\/localhost:8080/)) {
      route.continue()
    } else {
      // Блокируем все остальные (внешние)
      route.abort()
    }
  })

  // Мониторим выброшенные на странице ошибки JavaScript
  const errors = []
  page.on("pageerror", (error) => {
    errors.push(error)
  })

  await page.goto("http://localhost:8080")
  
  // Здесь мы проверяем, что в массиве ошибок нет никаких исключений
  // Иначе тест провалится!
  expect(errors.length).toBe(0) 
})
Результат: Если ваш встроенный JavaScript ожидает загрузки заблокированного скрипта и пытается вызвать из него несуществующую функцию, тест провалится, а в консоли вы увидите: ReferenceError: bootstrap is not defined (или любую другую ошибку, связанную с отсутствием внешней зависимости).
Таким образом, вы можете выполнять сквозные тесты (end-to-end) и убеждаться, что ваш основной функционал не зависит от наличия или корректной работы сторонних ресурсов.

Тестирование vs. Мониторинг

Тестирование с ограничениями ресурсов на этапе развертывания — отличный первый шаг. Оно гарантирует, что ваш код устойчив к медленным или недоступным зависимостям.
Однако это лишь первый шаг. E2E-тестирование проверяет работоспособность в один момент времени. Если вы полагаетесь на сторонние ресурсы, их простой может повлиять на ваш сайт уже завтра, после успешного деплоя.
Именно поэтому важно переходить от тестирования к мониторингу. Запускайте ваши Playwright-сценарии с измененными сетевыми настройками по расписанию, чтобы гарантировать, что ваш сайт и его внешние зависимости работают без сбоев 24/7. Будьте первым, кто узнает о проблемах у ваших поставщиков!

Заключение

  1. Проверяйте влияние: Если вы используете внешние ресурсы, узнайте, как их недоступность или замедление повлияет на ваш фронтенд. Отдавайте предпочтение self-хостингу, а если это невозможно — асинхронной и отказоустойчивой реализации.
  2. Тестируйте устойчивость: Используйте page.route в Playwright, чтобы имитировать медленную работу сети или блокировку ресурсов. Убедитесь, что ваш критический функционал продолжает работать.
  3. Мониторьте постоянно: Не полагайтесь только на тесты при деплое. Используйте Синтетические проверки Pingera, чтобы запускать ваши Playwright-сценарии по расписанию и оперативно выявлять любые проблемы.
Не повторяйте мою ошибку, когда пользователи часами смотрели на белый экран. Возьмите полный контроль над своими внешними зависимостями.