test: add playwright e2e tests for chat, auth, model selector, and api (#1355)

This commit is contained in:
josh 2025-12-15 14:43:39 +00:00 committed by GitHub
parent b1da86062e
commit 5f9e231788
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 260 additions and 416 deletions

View file

@ -1,113 +1,71 @@
import { expect, type Page } from "@playwright/test";
import type { Page } from "@playwright/test";
const CHAT_ID_REGEX =
/^http:\/\/localhost:3000\/chat\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
const MODEL_BUTTON_REGEX = /Gemini|Claude|GPT|Grok/i;
export class ChatPage {
private readonly page: Page;
page: Page;
constructor(page: Page) {
this.page = page;
}
get sendButton() {
return this.page.getByTestId("send-button");
}
get stopButton() {
return this.page.getByTestId("stop-button");
}
get multimodalInput() {
return this.page.getByTestId("multimodal-input");
}
get messagesContainer() {
return this.page.locator("[data-testid='messages-container']");
}
async goto() {
await this.page.goto("/");
await this.page.waitForLoadState("networkidle");
}
async createNewChat() {
await this.goto();
await this.page.goto("/");
await this.page.waitForSelector("[data-testid='multimodal-input']");
}
getCurrentURL(): string {
return this.page.url();
getInput() {
return this.page.getByTestId("multimodal-input");
}
async typeMessage(message: string) {
const input = this.getInput();
await input.fill(message);
}
async sendMessage() {
await this.page.getByTestId("send-button").click();
}
async sendUserMessage(message: string) {
await this.multimodalInput.click();
await this.multimodalInput.fill(message);
await this.sendButton.click();
await this.typeMessage(message);
await this.sendMessage();
}
async waitForResponse(timeout = 30_000) {
const response = await this.page.waitForResponse(
(res) => res.url().includes("/api/chat") && res.status() === 200,
{ timeout }
getSendButton() {
return this.page.getByTestId("send-button");
}
getStopButton() {
return this.page.getByTestId("stop-button");
}
async clickSuggestedAction(index = 0) {
const suggestions = this.page.locator(
"[data-testid='suggested-actions'] button"
);
await response.finished();
await suggestions.nth(index).click();
}
async isGenerationComplete(timeout = 30_000) {
await this.waitForResponse(timeout);
await this.page.waitForTimeout(500);
async openModelSelector() {
const modelButton = this.page
.locator("button")
.filter({ hasText: MODEL_BUTTON_REGEX })
.first();
await modelButton.click();
}
async hasChatIdInUrl() {
await expect(this.page).toHaveURL(CHAT_ID_REGEX);
async selectModel(modelName: string) {
await this.openModelSelector();
await this.page.getByText(modelName).first().click();
}
async getAssistantMessages() {
return await this.page.getByTestId("message-assistant").all();
}
async getUserMessages() {
return await this.page.getByTestId("message-user").all();
}
async getLastAssistantMessageContent(): Promise<string | null> {
const messages = await this.getAssistantMessages();
const lastMessage = messages.at(-1);
if (!lastMessage) {
return null;
}
const content = await lastMessage
.getByTestId("message-content")
.innerText();
return content;
}
async getLastUserMessageContent(): Promise<string | null> {
const messages = await this.getUserMessages();
const lastMessage = messages.at(-1);
if (!lastMessage) {
return null;
}
const content = await lastMessage
.getByTestId("message-content")
.innerText();
return content;
}
async isElementVisible(testId: string) {
await expect(this.page.getByTestId(testId)).toBeVisible();
}
async isElementNotVisible(testId: string) {
await expect(this.page.getByTestId(testId)).not.toBeVisible();
}
async expectToastToContain(text: string) {
await expect(this.page.getByTestId("toast")).toContainText(text);
}
async waitForInputToBeReady() {
await expect(this.multimodalInput).toBeVisible();
await expect(this.sendButton).toBeVisible();
async searchModels(query: string) {
await this.openModelSelector();
await this.page.getByPlaceholder("Search models...").fill(query);
}
}