test: add playwright e2e tests for chat, auth, model selector, and api (#1355)
This commit is contained in:
parent
b1da86062e
commit
5f9e231788
10 changed files with 260 additions and 416 deletions
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue