Browser interactions and HTTP
Use Playwright for controls and ctx.http for requests made by the website.
Interact with the page
await ctx.step("open-bookings", async () => {
const page = await ctx.browser();
await page.getByRole("link", { name: "Bookings" }).click();
await page.getByRole("heading", { name: "Your bookings" }).waitFor();
});Read JSON through the browser
const bookings = await ctx.step("read-bookings", async () => {
const response = await ctx.http.get("/api/bookings");
if (response.status !== 200) {
throw new FlowError("BOOKINGS_UNAVAILABLE", "Could not read bookings");
}
return Booking.array().parse(JSON.parse(response.body));
});Import FlowError from @browserlayer/sdk and your Booking schema. Navigate to the website before making relative requests.
| Response field | Type |
|---|---|
url | string |
status | number |
headers | string map |
body | string; parse JSON explicitly |
Requests run inside the page with its cookies and origin restrictions. HTTP error statuses are returned normally. Transport failures throw BROWSER_REQUEST_FAILED. There are no automatic request retries.
Submit a request
await ctx.step("submit", () =>
ctx.http.request("/api/preferences", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ language: "en" }),
timeoutMs: 20_000,
}),
);Observe after an action
await ctx.observe(
"confirmation",
async () => {
const page = await ctx.browser();
if (await page.getByText("Preferences saved").isVisible()) return true;
return undefined;
},
{ timeoutMs: 20_000 },
);Observation polls without repeating the preceding action. Keep it read-only. Timeout throws OBSERVATION_TIMEOUT.