The Cypress project has a sample test on how to intercept and modify AJAX calls and I wanted to rewrite the same example in Playwright for Python (pytest).
The example consists on intercepting and modifying an Ajax call that feeds the Comments section of a webpage.
This is the Cypress code. The test successfully passes.
it('cy.intercept() - route responses to matching requests', () => {
cy.visit('https://example.cypress.io/commands/network-requests')
let message = 'whoa, this comment does not exist'
// Stub a response to PUT comments/ ****
cy.intercept({
method: 'PUT',
url: '**/comments/*',
}, {
statusCode: 404,
body: { error: message },
headers: { 'access-control-allow-origin': '*' },
delayMs: 500,
}).as('putComment')
// we have code that puts a comment when the button is clicked in scripts.js
cy.get('.network-put').click()
cy.wait('@putComment')
// our 404 statusCode logic in scripts.js executed
cy.get('.network-put-comment').should('contain', message)
})
This is the Playwright for Python (pytest) version that I came up with:
def test_network_intercept(page: Page):
page.goto("https://example.cypress.io/commands/network-requests")
message = "whoa, this comment does not exist"
def handle_route(route: Route):
route.fulfill(
status=404,
body="{'error': '" + message + "'}",
headers={"access-control-allow-origin": "*"},
)
page.route("**/comments/*", handle_route)
page.locator(".network-put").click()
expect(page.locator(".network-put-comment")).to_contain_text(message)
This test fails in the last line
expect(page.locator(".network-put-comment")).to_contain_text(message)
Can anybody tell me what is wrong with my code and how to fix it ?
You're very close, but body="{'error': '" + message + "'}",
looks iffy. JSON doesn't allow single-quoted keys and values like that. Your response also doesn't specify the content-type
header as application/json
.
The following passes, using json=
rather than body=
, letting Playwright encode the dictionary into JSON and set the appropriate headers:
from playwright.sync_api import expect, Page, Route, sync_playwright # 1.37.0
def test_network_intercept(page: Page):
page.goto("https://example.cypress.io/commands/network-requests")
message = "whoa, this comment does not exist"
def handle_route(route: Route):
route.fulfill(
status=404,
json={"error": message},
headers={"access-control-allow-origin": "*"},
)
page.route("**/comments/*", handle_route)
page.locator(".network-put").click()
expect(page.locator(".network-put-comment")).to_contain_text(message)
if __name__ == "__main__":
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page()
test_network_intercept(page)
For reference, if you wanted to do what json=
does by hand, it'd look like:
import json
# ...
body=json.dumps({"error": message}), # or hand-encode it, but with double quotes
headers={"content-type": "application/json"}, # add appropriate response type
# ...
To help debug your original code, you can run headfully or add logging before goto
to see if there are errors in the console:
page.on("pageerror", lambda exc: print(f"uncaught exception: {exc}"))
page.goto() # ...
This gives uncaught exception: Cannot read properties of undefined (reading 'error')
. Not the clearest error, but indicates that the response body isn't coming through correctly.