| 1 |
1 |
import { test, expect } from "@playwright/test"; |
| 2 |
|
−import { TEST_EMAIL, TEST_PASSWORD, lastLinkFromLog, login, noConsoleErrors, readEnv, sql } from "./helpers"; |
|
2 |
+import { TEST_EMAIL, TEST_PASSWORD, lastLinkFromLog, login, noConsoleErrors, readEnv, restoreSession, saveSession, sql } from "./helpers"; |
| 3 |
3 |
|
| 4 |
4 |
/** |
| 5 |
5 |
* Full workflow against the dev server (EMAIL_DRY_RUN=1 EMAIL_DRY_RUN_PRINT=1 so links land in the log): |
| 52 |
52 |
|
| 53 |
53 |
test("add + validate provider keys (real providers)", async ({ page }) => { |
| 54 |
54 |
await login(page, email, password); |
|
55 |
+ await saveSession(page); |
| 55 |
56 |
await page.goto("/app/settings/providers"); |
| 56 |
57 |
const names: Record<string, RegExp> = { anthropic: /^Anthropic$/, openai: /^OpenAI$/, xai: /^xAI$/, gemini: /^Google Gemini$/ }; |
| 57 |
58 |
for (const [provider, envName] of [ |
| 80 |
81 |
}); |
| 81 |
82 |
|
| 82 |
83 |
test("select a model, send a message and receive a real stream", async ({ page }) => { |
| 83 |
|
− await login(page, email, password); |
|
84 |
+ await restoreSession(page); |
| 84 |
85 |
await page.goto("/app/chat"); |
| 85 |
86 |
await page.getByRole("button", { name: /select model/i }).click(); |
| 86 |
87 |
await page.getByLabel(/search models/i).fill("haiku 4.5"); |
| 98 |
99 |
}); |
| 99 |
100 |
|
| 100 |
101 |
test("conversation persists after reload and a second model works", async ({ page }) => { |
| 101 |
|
− await login(page, email, password); |
|
102 |
+ await restoreSession(page); |
| 102 |
103 |
await page.goto(conversationUrl); |
| 103 |
104 |
await expect(page.getByText(/PONG/).first()).toBeVisible(); |
| 104 |
105 |
// switch to an OpenAI model in the same conversation and continue |
| 114 |
115 |
}); |
| 115 |
116 |
|
| 116 |
117 |
test("model configuration is capability-driven", async ({ page }) => { |
| 117 |
|
− await login(page, email, password); |
|
118 |
+ await restoreSession(page); |
| 118 |
119 |
await page.goto("/app/chat"); |
| 119 |
120 |
await page.getByRole("button", { name: /select model/i }).click(); |
| 120 |
121 |
await page.getByLabel(/search models/i).fill("gpt-5.5"); |
| 124 |
125 |
await expect(page.getByText(/^Effort$/)).toBeVisible(); |
| 125 |
126 |
// seed/stop are not supported by the Responses API → never shown |
| 126 |
127 |
await expect(page.getByText(/^Seed$/)).toHaveCount(0); |
| 127 |
|
− await expect(page.getByText(/stop sequences/i)).toHaveCount(0); |
|
128 |
+ await expect(page.getByText(/^Stop sequences$/)).toHaveCount(0); |
|
129 |
+ await expect(page.getByText(/not available for this model/i)).toContainText(/seed/i); |
| 128 |
130 |
await page.keyboard.press("Escape"); |
| 129 |
131 |
}); |
| 130 |
132 |
|
| 131 |
133 |
test("usage dashboard shows the requests", async ({ page }) => { |
| 132 |
|
− await login(page, email, password); |
|
134 |
+ await restoreSession(page); |
| 133 |
135 |
await page.goto("/app/usage"); |
| 134 |
136 |
await expect(page.getByText(/requests/i).first()).toBeVisible(); |
| 135 |
137 |
await expect.poll(() => Number(sql(`select count(*) from usage_records ur join users u on u.id=ur.user_id where u.email='${email}'`))).toBeGreaterThanOrEqual(2); |
| 136 |
138 |
}); |
| 137 |
139 |
|
| 138 |
140 |
test("change password, logout, login again", async ({ page }) => { |
| 139 |
|
− await login(page, email, password); |
|
141 |
+ await restoreSession(page); |
| 140 |
142 |
await page.goto("/app/settings/security"); |
| 141 |
143 |
const newPassword = `${password}-2`; |
| 142 |
144 |
await page.getByLabel(/current password/i).fill(password); |