Playwright Network Monitoring and API Testing
This chapter introduces how to monitor browser network traffic and use Playwright for standalone API testing.
Listening to Network Requests and Responses
page.on('request') listening to requests
Example
test('Monitoring network requests', async ({ page }) => {
const requests = [];
// Listen to all requests
page.on('request', request => {
requests.push({
url: request.url(),
method: request.method(),
postData: request.postData(),
});
});
await page.goto('https://www.example.com/');
// Print all requests
console.log('The page made a total of', requests.length, 'requests');
requests.forEach(r => console.log(r.method, r.url));
});
const requests = [];
// Listen to all requests
page.on('request', request => {
requests.push({
url: request.url(),
method: request.method(),
postData: request.postData(),
});
});
await page.goto('https://www.example.com/');
// Print all requests
console.log('The page made a total of', requests.length, 'requests');
requests.forEach(r => console.log(r.method, r.url));
});
page.on('response') listening to responses
Example
test('Monitoring network responses', async ({ page }) => {
page.on('response', async response => {
// Only focus on API responses
if (response.url().includes('/api/')) {
console.log('API status code:', response.status());
console.log('API URL:', response.url());
}
});
await page.goto('https://www.example.com/');
});
page.on('response', async response => {
// Only focus on API responses
if (response.url().includes('/api/')) {
console.log('API status code:', response.status());
console.log('API URL:', response.url());
}
});
await page.goto('https://www.example.com/');
});
page.waitForResponse() waiting for a specific response
Example
test('Waiting for API response to complete', async ({ page }) => {
// Prepare to listen for the API response before clicking the button
const responsePromise = page.waitForResponse(
response => response.url().includes('/api/search') && response.status() === 200
);
// Trigger search
await page.getByPlaceholder('Search EXAMPLE tutorial').fill('Playwright');
await page.keyboard.press('Enter');
// Wait for the API response
const response = await responsePromise;
const data = await response.json();
// Verify the data returned by the API
expect(data.results.length).toBeGreaterThan(0);
});
// Prepare to listen for the API response before clicking the button
const responsePromise = page.waitForResponse(
response => response.url().includes('/api/search') && response.status() === 200
);
// Trigger search
await page.getByPlaceholder('Search EXAMPLE tutorial').fill('Playwright');
await page.keyboard.press('Enter');
// Wait for the API response
const response = await responsePromise;
const data = await response.json();
// Verify the data returned by the API
expect(data.results.length).toBeGreaterThan(0);
});
Console message listening
Example
test('Listening to console errors', async ({ page }) => {
const consoleErrors = [];
// Listen to console messages
page.on('console', msg => {
if (msg.type() === 'error') {
consoleErrors.push(msg.text());
}
});
await page.goto('https://www.example.com/');
// Assert that the page has no JavaScript errors
expect(consoleErrors).toHaveLength(0);
});
const consoleErrors = [];
// Listen to console messages
page.on('console', msg => {
if (msg.type() === 'error') {
consoleErrors.push(msg.text());
}
});
await page.goto('https://www.example.com/');
// Assert that the page has no JavaScript errors
expect(consoleErrors).toHaveLength(0);
});
ConsoleMessage type
| Type | Corresponding method |
|---|---|
'log' | console.log() |
'error' | console.error() |
'warning' | console.warn() |
'info' | console.info() |
'debug' | console.debug() |
API Testing (request fixture)
Playwright'srequestfixture allows you towithout going through the browserdirectly send HTTP requests.
This is ideal for preparing or cleaning up server-side state before and after tests.
Sending GET requests
Example
test('API GET request', async ({ request }) => {
const response = await request.get('https://jsonplaceholder.typicode.com/posts/1');
// Verify the response status
expect(response.status()).toBe(200);
// Get JSON data
const data = await response.json();
expect(data.id).toBe(1);
expect(data).toHaveProperty('title');
});
const response = await request.get('https://jsonplaceholder.typicode.com/posts/1');
// Verify the response status
expect(response.status()).toBe(200);
// Get JSON data
const data = await response.json();
expect(data.id).toBe(1);
expect(data).toHaveProperty('title');
});
Sending POST requests
Example
test('API POST request', async ({ request }) => {
const response = await request.post('https://jsonplaceholder.typicode.com/posts', {
data: {
title: 'EXAMPLE tutorial',
body: 'Playwright API testing example',
userId: 1,
},
});
expect(response.status()).toBe(201);
const data = await response.json();
expect(data.title).toBe('EXAMPLE tutorial');
});
const response = await request.post('https://jsonplaceholder.typicode.com/posts', {
data: {
title: 'EXAMPLE tutorial',
body: 'Playwright API testing example',
userId: 1,
},
});
expect(response.status()).toBe(201);
const data = await response.json();
expect(data.title).toBe('EXAMPLE tutorial');
});
Configuring baseURL and authentication headers
Example
// File path: playwright.config.ts
export default defineConfig({
use: {
// Base URL for API requests
baseURL: 'https://api.example.com',
// Default request headers
extraHTTPHeaders: {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Accept': 'application/json',
},
},
});
export default defineConfig({
use: {
// Base URL for API requests
baseURL: 'https://api.example.com',
// Default request headers
extraHTTPHeaders: {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Accept': 'application/json',
},
},
});
API + browser combined testing
In real projects, APIs are often used for state management before and after browser operations.
Example
test('API prepare data + browser verification', async ({ request, page }) => {
// Step 1: Create a test data record via API
const response = await request.post('/api/todos', {
data: { title: 'EXAMPLE test task', completed: false },
});
const todo = await response.json();
// Step 2: Verify the data is displayed in the browser
await page.goto('/todos');
await expect(page.getByText('EXAMPLE test task')).toBeVisible();
// Step 3: Clean up data via API
await request.delete(`/api/todos/${todo.id}`);
});
// Step 1: Create a test data record via API
const response = await request.post('/api/todos', {
data: { title: 'EXAMPLE test task', completed: false },
});
const todo = await response.json();
// Step 2: Verify the data is displayed in the browser
await page.goto('/todos');
await expect(page.getByText('EXAMPLE test task')).toBeVisible();
// Step 3: Clean up data via API
await request.delete(`/api/todos/${todo.id}`);
});
Other extensionsAPI testing is much faster than browser operations and is suitable for data preparation and cleanup. Combining API and browser operations can greatly improve test efficiency and reliability.