PRACTICAL GUIDE / Playwright network testing HAR WebSockets TLS
Debug the network layer without confusing HAR, sockets, and TLS
Build a Playwright network test that separates HTTP responses, WebSocket frames, TLS metadata, and mocks so each failure points to one layer.
In this guide6 sections
- Map the failure before choosing an API
- Capture HTTP, socket, and UI evidence in one attempt
- Distinguish publisher silence from authorization silence
- Diagnose TLS without asserting unstable infrastructure
- Separate lookalike failures with specific evidence
- Roll the diagnostics out without drowning CI
- Know when not to combine these techniques
What you will learn
- Map the failure before choosing an API
- Capture HTTP, socket, and UI evidence in one attempt
- Diagnose TLS without asserting unstable infrastructure
- Separate lookalike failures with specific evidence
The checkout page loads over HTTPS and its bootstrap API returns 200, but live inventory stays on “Connecting.” One engineer blames the certificate, another updates the HAR, and a third adds a WebSocket mock. All three changes can make a symptom disappear while leaving the actual failure untouched.
Network tests become useful when they keep protocol layers separate. An HTTP archive explains requests and responses. WebSocket events explain the message stream. TLS and server-address methods describe the connection visible to a response. The page assertion tells you whether any of that produced the customer result.
Map the failure before choosing an API
A browser workflow that uses live data crosses several boundaries:
- The browser resolves a hostname and opens a connection, possibly through a proxy or CDN.
- TLS validates the HTTPS or WSS endpoint and negotiates a protocol.
- An HTTP request receives a status, headers, and body.
- A WebSocket request may ask the server to upgrade the connection.
- The client sends subscription frames and receives application frames.
- JavaScript parses a message and updates product state.
Each Playwright signal answers a different question.
page.on('requestfailed') reports network-level failures such as name resolution, connection reset, or a browser error. It does not fire merely because the server returned 404 or 503. Those are valid HTTP responses from the transport's point of view, so inspect response.status().
Response.securityDetails() returns available SSL and certificate information for a browser response, or null for a non-HTTPS response. The object can include protocol, issuer, subject name, and certificate validity timestamps. Response.serverAddr() returns the visible IP address and port when available. Response.httpVersion(), added in Playwright 1.59, reports the HTTP version used by that response.
None of those response methods gives you a WebSocket message. For that, listen for page.on('websocket'), then attach handlers for sent frames, received frames, close, and socket error. A status 101 upgrade followed by zero frames is a different failure from a rejected upgrade.
HAR recording provides a reviewable network artifact. The first-class tracing.startHar() API arrived in 1.60, and Playwright 1.61 added WebSocket requests to HAR and trace recordings. Full mode retains more diagnostic fields. Minimal mode intentionally omits sizes, timing, cookies, security, and other data not required for HAR routing.
The product assertion still comes last in the mechanism and first in importance. A test should not pass because TLS 1.3 appeared or because a frame arrived. It should pass because the inventory value a customer sees changed to the correct value. Transport observations tell the engineer why that assertion passed or failed.
This model also stops overclaiming. TLS details from https://api.example.test/health prove facts about that response's connection. They do not automatically prove that wss://stream.example.test/live used the same certificate, edge, or origin. Even two URLs on one hostname can traverse different infrastructure after connection reuse, proxy routing, or service configuration.
Choose evidence according to risk. A feature test for a price badge may need one correlated received frame and the final text. A release smoke test for a certificate migration may need security details from each public hostname. A deterministic UI contract test may need mocks and no live TLS assertion at all.
Capture HTTP, socket, and UI evidence in one attempt
The following pattern records a filtered full HAR, observes request failures and WebSocket frames, captures connection metadata from the bootstrap response, and asserts the visible inventory update. Replace the application paths and message shape with your real contract. Every Playwright method and signature shown is public in version 1.61.
import { expect, test } from '@playwright/test';
test('explains the live inventory result', async ({ context, page }, testInfo) => {
const harPath = testInfo.outputPath('inventory-network.har');
const caseId = `${testInfo.testId}:${testInfo.retry}`;
const failedRequests: Array<{
url: string;
resourceType: string;
errorText?: string;
}> = [];
const sockets: Array<{
url: string;
sent: string[];
received: string[];
errors: string[];
closed: boolean;
}> = [];
page.on('requestfailed', request => {
failedRequests.push({
url: request.url(),
resourceType: request.resourceType(),
errorText: request.failure()?.errorText,
});
});
page.on('websocket', socket => {
const record = {
url: socket.url(),
sent: [] as string[],
received: [] as string[],
errors: [] as string[],
closed: false,
};
sockets.push(record);
socket.on('framesent', event => record.sent.push(event.payload.toString()));
socket.on('framereceived', event => record.received.push(event.payload.toString()));
socket.on('socketerror', error => record.errors.push(error));
socket.on('close', () => { record.closed = true; });
});
await context.tracing.startHar(harPath, {
mode: 'full',
content: 'omit',
urlFilter: /\/(?:api\/inventory|live\/inventory)(?:\?|$)/,
});
let networkSummary: unknown;
try {
const bootstrapPromise = page.waitForResponse(response =>
response.url().includes('/api/inventory') &&
response.request().method() === 'GET',
);
await page.goto(`/checkout?case=${encodeURIComponent(caseId)}`);
const bootstrap = await bootstrapPromise;
expect(bootstrap.status()).toBe(200);
const [security, server, httpVersion] = await Promise.all([
bootstrap.securityDetails(),
bootstrap.serverAddr(),
bootstrap.httpVersion(),
]);
expect(security, 'bootstrap should use HTTPS').not.toBeNull();
expect(security?.protocol).toMatch(/^TLS/);
expect(server, 'server address should be available').not.toBeNull();
expect(server?.port).toBeGreaterThan(0);
expect(httpVersion.length).toBeGreaterThan(0);
await expect(page.getByTestId('inventory-state')).toHaveText('Connected');
await page.getByRole('button', { name: 'Reserve item' }).click();
await expect(page.getByTestId('available-count')).toHaveText('9');
await expect.poll(
() => sockets.flatMap(socket => socket.received).join('\n'),
{ message: 'expected the inventory update frame' },
).toContain('inventory.updated');
networkSummary = {
caseId,
bootstrap: {
url: bootstrap.url(),
status: bootstrap.status(),
httpVersion,
security,
server,
},
failedRequests,
sockets,
};
} finally {
await context.tracing.stopHar();
await testInfo.attach('network-summary', {
body: Buffer.from(JSON.stringify(networkSummary ?? {
caseId,
failedRequests,
sockets,
}, null, 2)),
contentType: 'application/json',
});
await testInfo.attach('network-har', {
path: harPath,
contentType: 'application/json',
});
}
});Starting the response wait before navigation prevents a fast bootstrap request from escaping the listener. Starting HAR before navigation captures the same boundary. Socket handlers are registered before any application script can connect.
The filter includes both the HTTP bootstrap and WebSocket path. It omits page assets that do not help this failure, reducing artifact size and review time. content: 'omit' avoids response bodies, but it does not sanitize URLs, headers, or cookies. Treat the HAR as sensitive.
The finally block saves evidence even when the visible assertion fails. A JSON value cannot represent every internal Playwright object, so the summary selects stable primitive fields. It does not dump headers or full frames blindly.
A useful failed attachment might read:
{
"bootstrap": {
"status": 200,
"httpVersion": "h2",
"security": { "protocol": "TLS 1.3", "subjectName": "api.example.test" },
"server": { "ipAddress": "203.0.113.24", "port": 443 }
},
"failedRequests": [],
"sockets": [
{
"url": "wss://stream.example.test/live/inventory",
"sent": ["{\"type\":\"subscribe\"}"],
"received": [],
"errors": [],
"closed": true
}
]
}That record clears the bootstrap response and its TLS connection. It shows a socket was created and a subscription left the page, but no inventory frame returned before closure. The next owner is the streaming path, not the REST handler or DOM locator.
Distinguish publisher silence from authorization silence
An empty received array has a dangerous twin. The streaming publisher may have stopped producing updates after the server accepted the subscription. A different system can produce the same browser evidence when the HTTP bootstrap accepts its credentials but the streaming boundary rejects the socket identity or the subscription. A service that avoids detailed authorization errors may close quietly or leave the connection open without sending business frames. In both cases, the bootstrap is 200, the socket URL is present, sent contains the subscription, received is empty, and errors can remain empty.
Read those fields as a sequence, not a verdict. A healthy record has the expected URL in the first relevant sockets entry, the subscription in sent, then an application acknowledgement or update in received when that message is part of the product contract. The broken record stops after sent. The misleading value is sent itself: it proves that Playwright observed the browser send a frame, not that streaming ingress authenticated it, parsed it, or registered the subscription. Likewise, an empty errors array means no socket-error event reached the listener. It does not certify an application-level acceptance. A closed value of false only describes the attachment instant, so it cannot prove that the connection remained healthy after the assertion.
Separate the two causes with evidence from the streaming boundary. Correlate the narrow test interval and socket URL with the application's existing request or trace identifier when one is available. The ingress record should show whether identity validation succeeded and whether the requested subscription was registered. Then check whether the publisher produced the relevant event after that registration. Rejection or absence of registration points to credentials, tenant context, or subscription validation. Successful registration with no corresponding publication points upstream to the publisher. Registration plus a recorded publication, with no browser frame during the same interval, moves the investigation to fan-out, connection routing, or an intermediary. A HAR entry for a successful upgrade cannot make this distinction because authorization can happen in the first application frame after the upgrade.
Diagnose TLS without asserting unstable infrastructure
TLS checks often become brittle because a test pins details that are supposed to rotate. Certificate issuers can change during renewal. CDN edges use many IP addresses. HTTP versions can vary by browser, network path, proxy, and server capability. Those values are excellent evidence and poor universal snapshots.
Assert the stable policy instead. For a public HTTPS endpoint, useful release checks include:
- Security details are present rather than
null. - The reported protocol meets the organization's supported minimum.
- Certificate validity contains the current time with an agreed safety window.
- The subject name matches the hostname policy the platform team owns.
- The response status and product payload are correct.
Do not hard-code an issuer unless compliance or certificate pinning makes it a product contract. Do not assert one edge IP for a load-balanced service. Record those values so operations can correlate an incident.
Here is a focused API-request check using features added to APIResponse in Playwright 1.61. It targets a public HTTPS page so the snippet can run without the example application:
import { expect, test } from '@playwright/test';
test('reports TLS evidence for a public HTTPS endpoint', async ({ request }, testInfo) => {
const response = await request.get('https://example.com/');
try {
await expect(response).toBeOK();
const [security, server] = await Promise.all([
response.securityDetails(),
response.serverAddr(),
]);
expect(security, 'HTTPS response should expose security details').not.toBeNull();
expect(security?.protocol).toMatch(/^TLS/);
expect(server, 'server address should be available').not.toBeNull();
expect(server?.port).toBeGreaterThan(0);
const now = Math.floor(Date.now() / 1000);
expect(security?.validFrom).toBeLessThanOrEqual(now);
expect(security?.validTo).toBeGreaterThan(now + 7 * 24 * 60 * 60);
await testInfo.attach('api-connection', {
body: Buffer.from(JSON.stringify({
url: response.url(),
status: response.status(),
security,
server,
}, null, 2)),
contentType: 'application/json',
});
} finally {
await response.dispose();
}
});The seven-day certificate window is an example release policy, not a Playwright default. Pick a threshold with the platform team. A stricter window catches renewal risk earlier but can block a release during a planned certificate rotation.
APIResponse.securityDetails() follows redirects and reports information for the last request in the chain. If the test must validate every hop, disable automatic redirects where supported by your request setup or test each public URL directly. A healthy final endpoint can otherwise hide an unexpected clear-text or cross-domain redirect earlier in the chain.
Do not treat an APIResponse probe as interchangeable with a browser Response. The API request context and browser can differ in proxy configuration, cookies, client certificates, user agent, connection reuse, and network implementation. If customers reach the endpoint through a page, capture browser-response evidence in at least one test. Use the API probe for a direct service contract whose request configuration you control.
Record the request route beside the TLS result:
client: browser page
project: chromium
target: https://api.example.test/health
proxy mode: corporate-egress
redirects observed: 0
security protocol: TLS 1.3
visible server: 203.0.113.24:443That context prevents a direct API check from being used to close a browser-only incident. It also explains why a developer laptop and CI runner can report different visible server addresses while both satisfy the product contract.
Avoid ignoreHTTPSErrors: true in a certificate gate. That setting is useful when a local environment deliberately uses an untrusted certificate, but it removes the browser's normal rejection from the test. Put such environments in a clearly named project and keep at least one production-like project with ordinary verification.
Server address is similarly scoped. It identifies the server visible to the request. Behind a reverse proxy, that is often the proxy. It does not reveal the Kubernetes pod or application instance that eventually processed the request. Correlate an approved response header or server log if origin identity matters, and do not invent an origin guarantee from an edge IP.
Timing data helps when “TLS is slow” is only a guess. request.timing() breaks a browser request into available DNS, connect, secure-connect, request, and response milestones. Wait for the response to finish before reading the final values:
const responsePromise = page.waitForResponse(response =>
response.url().endsWith('/api/inventory') &&
response.request().method() === 'GET',
);
await page.reload();
const response = await responsePromise;
await response.finished();
const timing = response.request().timing();
const duration = (start: number, end: number) =>
start >= 0 && end >= 0 ? end - start : null;
const phases = {
dnsMs: duration(timing.domainLookupStart, timing.domainLookupEnd),
connectMs: duration(timing.connectStart, timing.connectEnd),
tlsMs: duration(timing.secureConnectionStart, timing.connectEnd),
timeToFirstByteMs: duration(timing.requestStart, timing.responseStart),
downloadMs: duration(timing.responseStart, timing.responseEnd),
};
console.log(JSON.stringify({
url: response.url(),
status: response.status(),
phases,
}, null, 2));An unavailable phase is represented by -1 in the raw timing object and becomes null in this summary. That does not automatically mean Playwright missed a TLS handshake. The browser may have reused an existing connection, served a response through a cache, or lacked a particular timing signal. Do not convert unavailable values to zero because zero implies an observed instantaneous phase.
Connection reuse also changes how you reproduce. A first navigation may pay DNS, TCP, and TLS costs, while the next API call rides an established HTTP/2 connection and reports no new connection phase. If the incident concerns cold-start TLS latency, use a fresh browser context and make the target request its first connection. If the customer problem appears after ten minutes on a warm page, preserving the warm connection is more representative.
A slow timeToFirstByteMs with no new connect phase points away from certificate negotiation and toward a proxy queue, server work, or upstream dependency. A large connectMs with a populated tlsMs calls for network and edge investigation. A large downloadMs belongs to response size or transfer behavior. These classifications are leads, not service-level assertions, because browser timings can be affected by cache, prioritization, and multiplexing.
Do not compare phase numbers from a HAR replay with a live run as if they share a clock. Replay serves recorded content through routing, and minimal HAR omits diagnostic timing. Keep a field such as networkMode: 'live' | 'har' in the attachment so charts never mix synthetic and real samples.
Separate lookalike failures with specific evidence
A 503 response and a refused connection both leave the UI without data. Playwright reports them differently.
For 503, a Response exists:
url: https://api.example.test/inventory
status: 503
requestfailed event: noneFor a connection failure, there may be no response:
url: https://api.example.test/inventory
status: unavailable
requestfailed: net::ERR_CONNECTION_REFUSEDDo not wait for requestfailed to catch an HTTP error. Assert the status or response.ok() explicitly.
A WebSocket can fail before Playwright exposes useful frame events. Capture failed requests whose resourceType() is websocket, plus the HAR request in Playwright 1.61. If no socket object appears and the HAR shows an authorization response, investigate handshake credentials. If the socket object appears, receives frames, and the UI remains stale, inspect application parsing.
A service worker creates another near-miss for ordinary HTTP routing. Requests handled inside a service worker can be invisible to page-level routing or belong to the worker rather than a frame. Playwright's network guide recommends serviceWorkers: 'block' when a service worker interferes with native routing. That is appropriate for a mock-focused test, but it changes the application architecture. Keep at least one service-worker-enabled test if offline or cache behavior matters.
HAR replay can make a TLS incident disappear because no live request reaches the affected server. Mark replay evidence in the test name and report. A response fulfilled from a file proves that the UI handles a recorded payload. It says nothing about current DNS, certificates, proxies, latency, or server availability.
WebSocket mocks have the same limitation. This self-contained test is valuable for a UI message contract and deliberately useless as a transport check:
import { expect, test } from '@playwright/test';
test('renders a controlled inventory message without a real network', async ({ page }) => {
await page.route('**/api/inventory', route => route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ available: 10 }),
}));
await page.routeWebSocket('**/live/inventory', socket => {
socket.onMessage(message => {
const parsed = JSON.parse(message.toString());
if (parsed.type === 'subscribe') {
socket.send(JSON.stringify({
type: 'inventory.updated',
available: 9,
}));
}
});
});
await page.goto('/checkout');
await expect(page.getByTestId('available-count')).toHaveText('9');
});The test is fast and deterministic. Its cost is coverage: no certificate, proxy, real upgrade, authentication, or backend publication is exercised. Keep that trade-off in the test name and suite documentation.
Roll the diagnostics out without drowning CI
Begin with the tests that have expensive or ambiguous failures. Add one filtered HAR and one small JSON summary to those cases. Do not turn on full HAR recording for every page in every browser before measuring artifact volume.
Treat the change as a telemetry migration for an existing suite. First land the bounded summary format, redaction rules, retry naming, and retention ownership without adding a new pass or fail condition. Exercise it on a small canary set and force one known failure so reviewers can confirm that an unsuccessful attempt keeps its own attachment. Next, install the observers before navigation in the remaining ambiguous tests, but keep their current product assertions unchanged. Only after those summaries survive parallel execution should full, filtered HAR capture be enabled for selected failures. Connection-policy assertions come last, and only for endpoints whose policy is an explicit contract.
Lifecycle mistakes break before protocol diagnosis does. A shared helper called after goto misses the socket that justified its existence. A custom fixture that closes the page or context before the test attachment is finalized leaves an incomplete record. A reporter or artifact job that retains only the final retry hides the initial incident. Detect those rollout defects by deliberately failing a canary before and after navigation, inspecting every retry directory, and checking that the summary names the expected socket even when the visible assertion fails. Do not expand the cohort until all three paths preserve evidence.
Frame detail creates a concrete choice. Retaining only direction, message type, and correlation data reduces storage and secret exposure, but it cannot explain a parser failure caused by one malformed payload field. Retaining full payloads preserves that clue, but every schema change adds redaction maintenance and every busy connection consumes more worker memory before attachment. Pick the richer capture only for the tests that investigate payload semantics, and make truncation visible so a missing late frame is not mistaken for server silence.
Use testInfo.outputPath() so parallel workers receive unique paths. Finalize the HAR in finally. Keep frame arrays bounded, because a busy socket can deliver thousands of messages during one test. Stop after the relevant event or retain only event type, correlation ID, sequence, and timestamp.
Set a retention rule based on result. A short summary can be kept for all runs. Raw full-mode HAR and trace files usually belong only on failures or selected canaries. Store sensitive artifacts behind access controls and expire them quickly.
Sanitize URLs, cookies, authorization headers, set-cookie responses, and message payloads before wider publication. content: 'omit' does not remove those fields. Use an allowlist for retained headers. Never print a signed WebSocket query string in plain terminal output.
Pin the Playwright version for the rollout. Browser Response.httpVersion() needs 1.59, tracing HAR needs 1.60, WebSocket requests in recordings and APIResponse security details need 1.61. Mixed clients produce different evidence for the same application behavior.
Compare added wall time and artifact bytes per test. Full recording costs I/O, upload time, storage, and reviewer attention. If a 200 KB frame summary answers the question, a 40 MB session archive is a liability.
Assign ownership by boundary. The application team owns the wrong DOM state after a correct frame. The streaming team owns missing or invalid business frames after a successful upgrade. The edge or platform team owns certificate and proxy policy. The test team owns late listeners, overbroad mocks, filters, and leaked artifacts. A report that makes this routing obvious pays for its recording cost.
When the boundary is still disputed, the team responsible for the failing customer journey should keep one incident owner instead of forwarding a raw HAR between queues. The test team first rules out listener timing, truncation, replay mode, and retry overwrite. A handoff then includes the test and retry identity, UTC observation interval, browser project, environment and proxy path, redacted endpoint, visible assertion, HTTP status or failure text, TLS and visible-server evidence, ordered sent and received frame excerpts, and any truncation marker. It must also name the existing service correlation value and the precise missing fact, such as “subscription registered, publication absent.” Artifact location, access scope, and expiry belong in the handoff so the receiving team can inspect evidence before retention removes it.
Retries need attempt-level separation. A first attempt can fail during TLS negotiation, then a retry can reuse a healthy edge and pass. If both summaries are written to network-summary.json, the passing retry overwrites the evidence that mattered. Include testInfo.retry in filenames or rely on testInfo.outputPath() from the current result directory, and confirm the reporter retains failed attempts.
For suites where this distinction matters, configure traces to retain the initial failure and retries according to the Playwright version in use:
import { defineConfig } from '@playwright/test';
export default defineConfig({
retries: process.env.CI ? 1 : 0,
use: {
trace: process.env.CI ? 'retain-on-failure-and-retries' : 'off',
},
});The trace mode records Playwright actions and network chronology. It does not remove the need for a bounded frame attachment when message content is the clue. Keep the HAR, frame summary, trace, and visible assertion under the same retry index.
Establish an artifact budget before the rollout. For example, allow one filtered HAR up to an agreed size and at most the first 50 relevant frames. If the limit is reached, add a truncation marker containing the total observed count. Silent truncation makes reviewers believe the stream ended; an explicit marker says the recorder stopped retaining data while the socket continued.
Run a secret-scanning check over sanitized examples, but do not treat a scanner as the sanitizer. Tokens in signed URLs or proprietary frame fields may not match known patterns. The owning team must define an allowlist of fields safe for retention.
Know when not to combine these techniques
Do not put a TLS assertion in every feature test. Certificate policy belongs in a small release or infrastructure suite. Repeating it across hundreds of cases adds noise and creates a wide failure blast radius during planned rotation.
Do not use a mock when the risk is transport behavior. A routed socket cannot validate WSS negotiation, proxy timeouts, load balancer affinity, or production authentication.
Do not use a live backend when the risk is a UI parser's response to a rare frame. A controlled socket route is faster, repeatable, and easier to make adverse on demand.
Avoid minimal HAR mode for a timing or security investigation because it intentionally omits those details. Prefer minimal mode for a compact replay fixture when diagnostic richness is not required.
Do not assert that an HTTPS health check proves a different WebSocket hostname. Test each public endpoint whose certificate or routing is a release contract.
This short, successful-path technique does not catch reconnection failure after an idle timeout, laptop sleep, or a network change. The initial upgrade and first update can be perfect while the client later reconnects without restoring its subscriptions. That risk needs a separate continuity scenario that keeps the connection alive or deliberately interrupts it, then proves that a later business update reaches the page. Making every feature test wait through an idle period would buy that coverage at the direct cost of suite duration and worker capacity.
Finally, do not let transport evidence replace the customer assertion. A perfect TLS handshake, status 200, upgrade request, and received frame can coexist with a broken screen. The suite earns confidence only when it proves the visible behavior and keeps enough layer-specific evidence to explain a failure without guessing.
// FIELD DISPATCH
Get the QA Field Notes
Weekly QA battles, AI testing guides, and interview drills. Free on Substack.
// LIVE COURSE / THE TESTING ACADEMY
Playwright Automation Mastery
Go beyond Selenium. Master Playwright with JS/TS in 90 days.
From the instructor behind this guide.
Playwright jobs are growing 8x faster than Selenium. 90 days / 75+ live hrs / Tue-Thu-Sat 7 AM IST.
PRIMARY REFERENCES
Verify the details at the source
QABattle guides are practical explanations. Product behavior, standards, and APIs can change, so use these primary references for the canonical details.
- 01Official playwright.dev reference
playwright.dev
Primary documentation selected and verified for the claims in this guide.
- 02Official playwright.dev reference
playwright.dev
Primary documentation selected and verified for the claims in this guide.
- 03Official playwright.dev reference
playwright.dev
Primary documentation selected and verified for the claims in this guide.
- 04Official playwright.dev reference
playwright.dev
Primary documentation selected and verified for the claims in this guide.
FAQ / QUICK ANSWERS
Questions testers ask
What should a Playwright network test assert besides a green page?
Assert the product outcome first, then retain the specific transport facts needed to explain it: HTTP status, failed-request text, socket frames, or TLS metadata. A single test rarely needs every available field.
Does response.securityDetails prove which origin server handled my request?
Not by itself. The method reports certificate and TLS information visible for that response, while `serverAddr()` commonly identifies a CDN, proxy, or load balancer address rather than the application origin.
Why does requestfailed not fire for a 503 response?
HTTP error responses are completed HTTP exchanges, so Playwright emits a response and request-finished lifecycle instead of treating them as transport failures. Check `response.status()` for 4xx and 5xx cases.
Can a HAR replay validate TLS and WebSocket production behavior?
No live server connection is required for a fulfilled HAR response, so replay cannot prove current certificate, proxy, DNS, or server behavior. Mock WebSocket messages separately and keep a smaller real-network suite for those risks.
Should CI fail when the server IP changes?
Only pin an address when infrastructure explicitly guarantees it. Most public services sit behind dynamic edges, so store `serverAddr()` as diagnostic evidence and assert a stable hostname, certificate policy, and product result instead.
RELATED GUIDES
Continue the learning route
GUIDE 01
Playwright Java Network Mocking and HAR Replay
Master Playwright Java network mocking HAR with practical examples, architecture decisions, failure analysis, CI guidance, metrics, and scenario-led interview answers.
GUIDE 02
Deterministic Network Tests with Playwright HAR Recording and Replay
Record, sanitize, and replay Playwright HAR fixtures for deterministic network tests, strict request matching, safer updates, and clear failures.
GUIDE 03
Playwright Tutorial: End-to-End Testing from Scratch
Playwright tutorial for beginners: install, write your first test, TypeScript setup, codegen, fixtures, API testing, debugging, and CI tips in one guide.
GUIDE 04
Playwright ariaSnapshot Boxes for AI Testing
Learn Playwright ariaSnapshot boxes AI testing with working code, failure cases, debugging steps, and CI evidence for reliable QA automation.
GUIDE 05
Playwright Network Virtualization Interview Questions
Playwright network virtualization interview questions: practical design, implementation, debugging, CI, metrics, and interview guidance for QA, SDET, and automation.