IKivan-kozenko -aqa
Try yourself as a QA tester
All topics·Intermediate·22 / 27

Evaluating JavaScript

Your test code runs in Node.js. The page runs in the browser. They're separate processes — variables don't cross that boundary automatically. page.evaluate() is the bridge: pass a function, execute it in the browser, get the result back in Node.

Two environments — the most important thing to understand

Test code runs in Node.js; evaluate() code runs in the browser — pass data explicitly, return serializable values

Your test runs in Node.js. The page runs in a browser (V8, Blink). They're separate VMs — JavaScript closures don't cross between them. When you write a function inside page.evaluate(), that function runs in the browser. It has access to window, document, localStorage — but NOT to variables from your test unless you pass them explicitly.

ts
const orderId = 'ORD-042'

// ❌ НЕПРАВИЛЬНО — orderId недоступна в браузері
const title = await page.evaluate(() => {
  return document.querySelector(`[data-order="${orderId}"]`)?.textContent
  //                                      ^^^^^^^ ReferenceError: orderId is not defined
})

// ✅ ПРАВИЛЬНО — передати явно другим аргументом
const title = await page.evaluate((id) => {
  return document.querySelector(`[data-order="${id}"]`)?.textContent
}, orderId)

page.evaluate() — read browser state

Use page.evaluate() when you need to read something from the browser that Playwright's locators can't reach — like window.__APP_CONFIG__, localStorage, computed styles, or scroll position.

ts
// Прочитати з window
const appVersion = await page.evaluate(() => window.__APP_CONFIG__.version)
expect(appVersion).toBe('2.4.1')

// Прочитати з localStorage
const token = await page.evaluate(() => localStorage.getItem('auth_token'))
expect(token).not.toBeNull()

// Прочитати URL
const href = await page.evaluate(() => document.location.href)

// Перевірити позицію скролу
const scrollY = await page.evaluate(() => window.scrollY)
expect(scrollY).toBeGreaterThan(0)

// Прочитати computed стиль — те що CSS реально застосував
const color = await page.evaluate(() => {
  const btn = document.querySelector('[data-testid="primary-btn"]')
  return window.getComputedStyle(btn!).backgroundColor
})
expect(color).toBe('rgb(59, 130, 246)')

page.evaluate() — trigger browser-level actions

Sometimes I need to trigger something that only works from inside the browser — dispatch a custom event, call a global app method, or simulate a scroll. evaluate is the right tool.

ts
// Тригернути кастомну подію (для тестування event listeners)
await page.evaluate(() => {
  window.dispatchEvent(new CustomEvent('order:updated', {
    detail: { orderId: 'ORD-042', status: 'shipped' }
  }))
})

// Скролити до низу сторінки
await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight))

// Викликати глобальний метод застосунку (якщо є)
await page.evaluate(() => window.__APP__.resetState())

// Очистити localStorage перед тестом
await page.evaluate(() => localStorage.clear())

page.addInitScript() — inject before any page JS runs

page.evaluate() runs after the page loads. If you need code to run before any page JavaScript — to mock a browser API, replace Math.random, or set up a global variable — use page.addInitScript(). It runs at navigation time, before the page's own scripts.

ts
// Зробити Math.random() детермінованим
test.beforeEach(async ({ page }) => {
  await page.addInitScript(() => {
    Math.random = () => 0.42 // завжди 0.42 — стабільні тести рандомних речей
  })
})

// Передати значення з тесту
const seed = 12345
await page.addInitScript((seedValue) => {
  Math.random = () => (seedValue % 100) / 100
}, seed)

// Для всього контексту — застосовується до кожної нової сторінки
await context.addInitScript(() => {
  window.__TEST_MODE__ = true
})

Async evaluate — fetch inside the browser

The function inside evaluate can be async. Playwright waits for the promise to resolve. Use this when you need to make a request from the browser context — with the browser's cookies and session — instead of from Node.js.

ts
// Зробити запит від імені браузера (з його cookies/session)
const apiData = await page.evaluate(async () => {
  const response = await fetch('/api/orders?limit=5')
  return response.json()
})
expect(apiData).toHaveLength(5)

// Перевірити статус ендпоїнту зсередини браузера
const status = await page.evaluate(async (endpoint) => {
  const res = await fetch(endpoint)
  return res.status
}, '/api/health')
expect(status).toBe(200)