Explorer reports addition
We have added a new Explorer feature to reports, with a timeline scrubber and easy anomaly detection.
Basic browser performance testing with Core Web Vitals and loading metrics using Playwright
LoadForge can record your browser, graphically build tests, scan your site with a wizard and more. Sign up now to run your first test.
This guide shows how to measure basic browser performance metrics including Core Web Vitals. Perfect for monitoring page loading speed and user experience.
from locust import task, HttpUser
from locust_plugins.users.playwright import PlaywrightUser, pw
import time
class PerformanceUser(PlaywrightUser):
def on_start(self):
# Pages to test performance
self.test_pages = [
"/",
"/products",
"/about",
"/blog",
"/contact"
]
@task(3)
@pw
def measure_core_web_vitals(self, page):
"""Measure Core Web Vitals for a page"""
test_url = self.random_page()
# Start timing
start_time = time.time()
# Navigate to page
page.goto(test_url)
# Wait for page to fully load
page.wait_for_load_state("networkidle")
# Measure First Contentful Paint (FCP)
fcp = page.evaluate("""
() => {
const entries = performance.getEntriesByType('paint');
const fcpEntry = entries.find(entry => entry.name === 'first-contentful-paint');
return fcpEntry ? fcpEntry.startTime : null;
}
""")
# Measure Largest Contentful Paint (LCP)
lcp = page.evaluate("""
() => {
return new Promise((resolve) => {
const observer = new PerformanceObserver((list) => {
const entries = list.getEntries();
const lastEntry = entries[entries.length - 1];
resolve(lastEntry.startTime);
});
observer.observe({entryTypes: ['largest-contentful-paint']});
// Fallback timeout
setTimeout(() => resolve(null), 3000);
});
}
""")
# Measure Cumulative Layout Shift (CLS)
cls = page.evaluate("""
() => {
let clsValue = 0;
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (!entry.hadRecentInput) {
clsValue += entry.value;
}
}
});
observer.observe({entryTypes: ['layout-shift']});
return new Promise((resolve) => {
setTimeout(() => resolve(clsValue), 2000);
});
}
""")
# Calculate total load time
load_time = (time.time() - start_time) * 1000
# Log results
print(f"Performance for {test_url}:")
print(f" Load Time: {load_time:.0f}ms")
if fcp:
print(f" FCP: {fcp:.0f}ms")
if lcp:
print(f" LCP: {lcp:.0f}ms")
if cls:
print(f" CLS: {cls:.3f}")
@task(2)
@pw
def measure_resource_loading(self, page):
"""Measure resource loading performance"""
test_url = self.random_page()
# Track network requests
requests = []
def handle_request(request):
requests.append({
'url': request.url,
'method': request.method,
'start_time': time.time()
})
def handle_response(response):
# Find matching request
for req in requests:
if req['url'] == response.url:
req['response_time'] = time.time() - req['start_time']
req['status'] = response.status
req['size'] = len(response.body()) if response.body() else 0
break
page.on('request', handle_request)
page.on('response', handle_response)
# Navigate and measure
page.goto(test_url)
page.wait_for_load_state("networkidle")
# Analyze resource performance
total_size = sum(req.get('size', 0) for req in requests)
slow_resources = [req for req in requests if req.get('response_time', 0) > 2.0]
print(f"Resource loading for {test_url}:")
print(f" Total requests: {len(requests)}")
print(f" Total size: {total_size / 1024:.1f}KB")
print(f" Slow resources (>2s): {len(slow_resources)}")
@task(2)
@pw
def test_page_speed(self, page):
"""Test basic page loading speed"""
test_url = self.random_page()
# Measure different loading stages
start_time = time.time()
# Navigate to page
page.goto(test_url)
# Measure time to DOM content loaded
dom_time = time.time()
# Wait for all resources
page.wait_for_load_state("networkidle")
# Measure total load time
total_time = time.time()
# Calculate metrics
dom_load_time = (dom_time - start_time) * 1000
total_load_time = (total_time - start_time) * 1000
# Get page size
page_size = page.evaluate("document.documentElement.outerHTML.length")
print(f"Page speed for {test_url}:")
print(f" DOM Load: {dom_load_time:.0f}ms")
print(f" Total Load: {total_load_time:.0f}ms")
print(f" Page Size: {page_size / 1024:.1f}KB")
# Performance thresholds
if total_load_time > 3000:
print(f" WARNING: Slow loading page ({total_load_time:.0f}ms)")
@task(1)
@pw
def test_mobile_performance(self, page):
"""Test performance on mobile viewport"""
test_url = self.random_page()
# Set mobile viewport
page.set_viewport_size(width=375, height=667)
# Simulate slower mobile connection
page.route("**/*", lambda route: (
time.sleep(0.1), # Add 100ms delay
route.continue_()
))
start_time = time.time()
# Navigate and measure
page.goto(test_url)
page.wait_for_load_state("networkidle")
load_time = (time.time() - start_time) * 1000
print(f"Mobile performance for {test_url}:")
print(f" Mobile Load Time: {load_time:.0f}ms")
# Mobile performance is typically slower
if load_time > 5000:
print(f" WARNING: Very slow on mobile ({load_time:.0f}ms)")
def random_page(self):
"""Get a random test page"""
import random
return random.choice(self.test_pages)