Files
next-ai-draw-io/tests/e2e/chat-errors.spec.ts
T
Dayuan Jiang 17ea4811fb feat(settings): reorganize the settings dialog and the model settings (#958)
Tabs Models, General, Drawing and About, each with a fixed title row.

The models tab becomes three pages: the user's providers with one status
each and the server's models; a searchable picker of all 24 providers in
four groups; and a provider page in three steps (connection, models,
test). A new provider gets its first suggested model, and a passing test
offers to use the model in the chat. Bedrock asks for one way of signing
in, with an optional session token for temporary access keys.

The composer's model picker lists every model, marking untested and
failed ones, and can open the provider picker. A chat error that offers
model settings opens the provider the request went to.
2026-10-08 18:10:07 +09:00

113 lines
4.1 KiB
TypeScript

import { expect, type Page, test } from "@playwright/test"
import { getIframe, sendMessage } from "./lib/fixtures"
/** Answer the chat request with the given response and send a message */
async function chatWith(
page: Page,
response: { status: number; contentType: string; body: string },
) {
await page.route("**/api/chat", (route) => route.fulfill(response))
await page.goto("/", { waitUntil: "networkidle" })
await getIframe(page).waitFor({ state: "visible", timeout: 30000 })
await sendMessage(page, "Draw a box")
}
test("a rejected key shows a hint, the provider's words and a settings button", async ({
page,
}) => {
await chatWith(page, {
status: 401,
contentType: "application/json",
body: JSON.stringify({
type: "provider",
code: "invalid_api_key",
message: "Authentication Fails, Your api key is invalid",
}),
})
await expect(
page.getByText("The provider rejected the API key"),
).toBeVisible({
timeout: 15000,
})
// First match: the Next.js dev overlay at the end also lists the error
await expect(page.getByText("Authentication Fails").first()).toBeVisible()
await page.getByRole("button", { name: "Open model settings" }).click()
// A server model answered: settings opens on the models tab's list
const dialog = page.getByRole("dialog", { name: "Settings" })
await expect(dialog).toBeVisible()
await expect(
dialog.getByRole("heading", { name: "Models", exact: true }),
).toBeVisible()
await expect(dialog.getByText("Use your own API key")).toBeVisible()
})
test("an error before the stream shows its text, not raw JSON", async ({
page,
}) => {
await chatWith(page, {
status: 400,
contentType: "application/json",
body: JSON.stringify({
error: 'Model "x" is not available on the server',
}),
})
// The whole message, not the JSON around it (the Next.js dev overlay
// also lists the raw console error; it is not part of the app)
await expect(
page.getByText('Model "x" is not available on the server', {
exact: true,
}),
).toBeVisible({ timeout: 15000 })
})
test("a provider rate limit is not shown as this site's quota", async ({
page,
}) => {
const errorText = JSON.stringify({
type: "provider",
code: "rate_limited",
message: "Rate limit exceeded for your organization",
})
await chatWith(page, {
status: 200,
contentType: "text/event-stream",
body: `data: {"type":"start"}\n\ndata: ${JSON.stringify({ type: "error", errorText })}\n\ndata: [DONE]\n\n`,
})
await expect(
page.getByText("The provider is limiting requests"),
).toBeVisible({ timeout: 15000 })
// The site's own tokens-per-minute toast
await expect(page.getByText("Rate limit reached")).toHaveCount(0)
})
test("a refused server key shows only the quota hint and a settings button", async ({
page,
}) => {
// What the chat route streams when the server's key gets a 403, e.g.
// after a daily spend cap blocked it
const errorText = JSON.stringify({
type: "provider",
code: "server_key_forbidden",
message: "",
})
await chatWith(page, {
status: 200,
contentType: "text/event-stream",
body: `data: {"type":"start"}\n\ndata: ${JSON.stringify({ type: "error", errorText })}\n\ndata: [DONE]\n\n`,
})
await expect(
page.getByText("Today's free quota is used up", { exact: false }),
).toBeVisible({ timeout: 15000 })
await expect(page.getByText("The provider returned an error")).toHaveCount(
0,
)
await page.getByRole("button", { name: "Open model settings" }).click()
// A server model answered: settings opens on the models tab's list
const dialog = page.getByRole("dialog", { name: "Settings" })
await expect(dialog).toBeVisible()
await expect(
dialog.getByRole("heading", { name: "Models", exact: true }),
).toBeVisible()
await expect(dialog.getByText("Use your own API key")).toBeVisible()
})