Compare commits

...

16 Commits

Author SHA1 Message Date
Raunak Bhagat
5b422703e0 Fix build failure 2025-10-07 14:11:51 -07:00
Raunak Bhagat
8127b64606 Merge branch 'main' into fix-tests-2 2025-10-07 14:08:22 -07:00
Raunak Bhagat
f78f4f55b9 Fix more tests 2025-10-07 14:00:39 -07:00
Raunak Bhagat
b0b1582dfd Fix smaller tests 2025-10-07 13:31:04 -07:00
Raunak Bhagat
b5800633c5 Update import shortcuts 2025-10-07 13:27:36 -07:00
Raunak Bhagat
e1922c4325 Remove commented out code 2025-10-07 13:26:58 -07:00
Raunak Bhagat
2031bd54bd Merge branch 'main' into fix-tests 2025-10-07 13:17:09 -07:00
Raunak Bhagat
090325a01d Fix some more tests 2025-10-07 13:11:43 -07:00
Raunak Bhagat
af83a2c113 Merge branch 'main' into fix-tests 2025-10-07 12:02:55 -07:00
Raunak Bhagat
05e163931e Fix message regeneration tests 2025-10-07 12:02:11 -07:00
Raunak Bhagat
bddb140d3d Fix failing tests 2025-10-06 23:38:40 -07:00
Raunak Bhagat
5611890570 Merge branch 'main' into fix-tests 2025-10-06 22:21:12 -07:00
Raunak Bhagat
4245173a09 Run prettier 2025-10-06 22:16:40 -07:00
Raunak Bhagat
015b52cf9a Fix tests 2025-10-06 21:46:21 -07:00
Raunak Bhagat
7335e4e2ec Re-add some tags back 2025-10-06 21:22:15 -07:00
Raunak Bhagat
2711a51cb5 Help fix some failing tests 2025-10-06 14:33:56 -07:00
23 changed files with 137 additions and 158 deletions

View File

@@ -7,7 +7,7 @@ import {
getAuthUrlSS,
} from "@/lib/userSS";
import { redirect } from "next/navigation";
import { EmailPasswordForm } from "../login/EmailPasswordForm";
import EmailPasswordForm from "../login/EmailPasswordForm";
import SignInButton from "@/app/auth/login/SignInButton";
import AuthFlowContainer from "@/components/auth/AuthFlowContainer";
import AuthErrorDisplay from "@/components/auth/AuthErrorDisplay";

View File

@@ -21,7 +21,7 @@ interface EmailPasswordFormProps {
isJoin?: boolean;
}
export function EmailPasswordForm({
export default function EmailPasswordForm({
isSignup = false,
shouldVerify,
referralSource,
@@ -32,10 +32,12 @@ export function EmailPasswordForm({
const { user } = useUser();
const { popup, setPopup } = usePopup();
const [isWorking, setIsWorking] = useState<boolean>(false);
return (
<>
{isWorking && <Spinner />}
{popup}
<Formik
initialValues={{
email: defaultEmail ? defaultEmail.toLowerCase() : "",
@@ -127,13 +129,14 @@ export function EmailPasswordForm({
}
}}
>
{({ isSubmitting, values }) => (
{({ isSubmitting }) => (
<Form>
<TextFormField
name="email"
label="Email"
type="email"
placeholder="email@yourcompany.com"
data-testid="email"
/>
<TextFormField
@@ -141,6 +144,7 @@ export function EmailPasswordForm({
label="Password"
type="password"
placeholder="**************"
data-testid="password"
/>
<Button type="submit" className="w-full" disabled={isSubmitting}>

View File

@@ -4,7 +4,7 @@ import { AuthTypeMetadata } from "@/lib/userSS";
import LoginText from "@/app/auth/login/LoginText";
import Link from "next/link";
import SignInButton from "@/app/auth/login/SignInButton";
import { EmailPasswordForm } from "./EmailPasswordForm";
import EmailPasswordForm from "./EmailPasswordForm";
import { NEXT_PUBLIC_FORGOT_PASSWORD_ENABLED } from "@/lib/constants";
import { useSendAuthRequiredMessage } from "@/lib/extension/utils";
import Text from "@/refresh-components/Text";

View File

@@ -10,9 +10,11 @@ import { redirect } from "next/navigation";
import AuthFlowContainer from "@/components/auth/AuthFlowContainer";
import LoginPage from "./LoginPage";
const Page = async (props: {
export interface PageProps {
searchParams?: Promise<{ [key: string]: string | string[] | undefined }>;
}) => {
}
export default async function Page(props: PageProps) {
const searchParams = await props.searchParams;
const autoRedirectDisabled = searchParams?.disableAutoRedirect === "true";
const nextUrl = Array.isArray(searchParams?.next)
@@ -85,6 +87,4 @@ const Page = async (props: {
</AuthFlowContainer>
</div>
);
};
export default Page;
}

View File

@@ -7,7 +7,7 @@ import {
getAuthUrlSS,
} from "@/lib/userSS";
import { redirect } from "next/navigation";
import { EmailPasswordForm } from "../login/EmailPasswordForm";
import EmailPasswordForm from "../login/EmailPasswordForm";
import SignInButton from "@/app/auth/login/SignInButton";
import AuthFlowContainer from "@/components/auth/AuthFlowContainer";
import ReferralSourceSelector from "./ReferralSourceSelector";

View File

@@ -107,10 +107,10 @@ export const MessagesDisplay: React.FC<MessagesDisplayProps> = ({
);
const handleEditWithMessageId = useCallback(
(editedContent: string, msgId: number | null | undefined) => {
(editedContent: string, msgId: number) => {
onSubmit({
message: editedContent,
messageIdToResend: msgId || undefined,
messageIdToResend: msgId,
currentMessageFiles: [],
useAgentSearch: deepResearchEnabled,
});

View File

@@ -548,6 +548,7 @@ function ChatInputBarInner({
<div className="flex flex-row items-center gap-spacing-inline">
<LLMPopover requiresImageGeneration />
<IconButton
id="onyx-chat-input-send-button"
icon={chatState === "input" ? SvgArrowUp : SvgStop}
disabled={chatState === "input" && !message}
onClick={() => {

View File

@@ -296,7 +296,7 @@ export function UserSettings({
return (
<div className="flex flex-col p-padding-content">
{/* {(showPasswordSection || hasConnectors) && (
{(showPasswordSection || hasConnectors) && (
<div className="w-1/4 pr-4 flex-shrink-0">
<nav>
<ul className="space-y-2">
@@ -343,7 +343,7 @@ export function UserSettings({
</ul>
</nav>
</div>
)} */}
)}
<div
className={`${
showPasswordSection || hasConnectors ? "w-3/4" : "w-full"

View File

@@ -275,6 +275,7 @@ export default function HumanMessage({
tertiary
tooltip="Copy"
onClick={() => copyAll(content)}
data-testid="HumanMessage/copy-button"
/>
<IconButton
icon={SvgEdit}
@@ -284,6 +285,7 @@ export default function HumanMessage({
setIsEditing(true);
setIsHovered(false);
}}
data-testid="HumanMessage/edit-button"
/>
</div>
) : (

View File

@@ -20,10 +20,7 @@ interface InternalMemoizedHumanMessageProps
}
interface MemoizedHumanMessageProps extends BaseMemoizedHumanMessageProps {
handleEditWithMessageId: (
editedContent: string,
messageId: number | null | undefined
) => void;
handleEditWithMessageId: (editedContent: string, messageId: number) => void;
}
const _MemoizedHumanMessage = React.memo(function _MemoizedHumanMessage({
@@ -65,7 +62,14 @@ export const MemoizedHumanMessage = ({
}: MemoizedHumanMessageProps) => {
const onEdit = useCallback(
(editedContent: string) => {
handleEditWithMessageId(editedContent, messageId ?? undefined);
if (!messageId) {
console.warn(
"No message id specified; cannot edit an undefined message"
);
return;
}
handleEditWithMessageId(editedContent, messageId);
},
[handleEditWithMessageId, messageId]
);

View File

@@ -30,7 +30,10 @@ export default function MessageSwitcher({
const next = handle(totalPages, handleNext);
return (
<div className="flex flex-row items-center gap-spacing-inline">
<div
className="flex flex-row items-center gap-spacing-inline"
data-testid="MessageSwitcher/container"
>
<IconButton
icon={SvgChevronLeft}
onClick={previous}

View File

@@ -358,6 +358,7 @@ export default function AIMessage({
onClick={() => copyAll(getTextContent(rawPackets))}
tertiary
tooltip="Copy"
data-testid="AIMessage/copy-button"
/>
<IconButton
icon={SvgThumbsUp}
@@ -369,6 +370,7 @@ export default function AIMessage({
}
tertiary
tooltip="Good Response"
data-testid="AIMessage/like-button"
/>
<IconButton
icon={SvgThumbsDown}
@@ -380,18 +382,21 @@ export default function AIMessage({
}
tertiary
tooltip="Bad Response"
data-testid="AIMessage/dislike-button"
/>
{chatState.regenerate && (
<LLMPopover
currentModelName={chatState.overriddenModel}
onSelect={(modelName) => {
const llmDescriptor =
parseLlmDescriptor(modelName);
chatState.regenerate!(llmDescriptor);
}}
folded
/>
<div data-testid="AIMessage/regenerate-button">
<LLMPopover
currentModelName={chatState.overriddenModel}
onSelect={(modelName) => {
const llmDescriptor =
parseLlmDescriptor(modelName);
chatState.regenerate!(llmDescriptor);
}}
folded
/>
</div>
)}
{nodeId &&

View File

@@ -117,7 +117,7 @@ export default function LLMPopover({
return (
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<div>{triggerContent}</div>
<div data-testid="llm-popover-trigger">{triggerContent}</div>
</PopoverTrigger>
<PopoverContent
side="top"

View File

@@ -74,7 +74,7 @@ const iconClasses = (active: boolean | undefined) =>
}) as const;
export interface IconButtonProps
extends React.HTMLAttributes<HTMLButtonElement> {
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
// Button states:
active?: boolean;
disabled?: boolean;

View File

@@ -1,7 +1,6 @@
"use client";
import React, { useMemo, useState } from "react";
import { useRouter } from "next/navigation";
import AgentCard from "@/sections/AgentsModal/AgentCard";
import { useUser } from "@/components/user/UserProvider";
import { checkUserOwnsAssistant as checkUserOwnsAgent } from "@/lib/assistants/checkOwnership";
@@ -13,7 +12,7 @@ import { ModalIds, useModal } from "@/refresh-components/contexts/ModalContext";
import SvgFilter from "@/icons/filter";
import SvgOnyxOctagon from "@/icons/onyx-octagon";
import Button from "@/refresh-components/buttons/Button";
import Link from "next/link";
import InputTypeIn from "@/refresh-components/inputs/InputTypeIn";
interface AgentsSectionProps {
title: string;
@@ -95,7 +94,6 @@ function useAgentFilters() {
export default function AgentsModal() {
const { agents, pinnedAgents } = useAgentsContext();
const { agentFilters, toggleAgentFilter } = useAgentFilters();
const router = useRouter();
const { user } = useUser();
const [searchQuery, setSearchQuery] = useState("");
const { toggleModal } = useModal();
@@ -144,15 +142,17 @@ export default function AgentsModal() {
<Modal id={ModalIds.AgentsModal} icon={SvgOnyxOctagon} title="Agents" sm>
<div className="flex flex-col sticky top-[0rem] z-10 bg-background-tint-01 p-spacing-paragraph">
<div className="flex flex-row items-center gap-spacing-interline">
<input
className="w-full h-[3rem] border bg-transparent rounded-08 p-padding-button"
<InputTypeIn
placeholder="Search..."
value={searchQuery}
onChange={(event) => setSearchQuery(event.target.value)}
/>
<Link href="/assistants/new">
<Button className="h-full">Create</Button>
</Link>
<Button
href="/assistants/new"
onClick={() => toggleModal(ModalIds.AgentsModal, false)}
>
Create
</Button>
</div>
<div className="py-padding-content flex items-center gap-spacing-interline flex-wrap">

View File

@@ -545,16 +545,18 @@ function AppSidebarInner() {
<SidebarWrapper folded={folded} setFolded={setFolded}>
<div className="flex flex-col gap-spacing-interline">
<NavigationTab
icon={SvgEditBig}
className="!w-full"
folded={folded}
onClick={() => route({})}
active={Array.from(searchParams).length === 0}
tooltip
>
New Session
</NavigationTab>
<div data-testid="AppSidebar/new-session">
<NavigationTab
icon={SvgEditBig}
className="!w-full"
folded={folded}
onClick={() => route({})}
active={Array.from(searchParams).length === 0}
tooltip
>
New Session
</NavigationTab>
</div>
{folded && (
<>
@@ -600,13 +602,15 @@ function AppSidebarInner() {
))}
</SortableContext>
</DndContext>
<NavigationTab
icon={SvgMoreHorizontal}
onClick={() => toggleModal(ModalIds.AgentsModal, true)}
lowlight
>
More Agents
</NavigationTab>
<div data-testid="AppSidebar/more-agents">
<NavigationTab
icon={SvgMoreHorizontal}
onClick={() => toggleModal(ModalIds.AgentsModal, true)}
lowlight
>
More Agents
</NavigationTab>
</div>
</SidebarSection>
<SidebarSection title="Projects">

View File

@@ -101,13 +101,11 @@ function SettingsPopover({
Curator Panel
</NavigationTab>
),
<NavigationTab
key="user-settings"
icon={SvgUser}
onClick={onUserSettingsClick}
>
User Settings
</NavigationTab>,
<div key="user-settings" data-testid="Settings/user-settings">
<NavigationTab icon={SvgUser} onClick={onUserSettingsClick}>
User Settings
</NavigationTab>
</div>,
<NavigationTab
key="notifications"
icon={SvgBell}
@@ -216,7 +214,7 @@ export default function Settings({
}
>
<PopoverTrigger asChild>
<div className="flex flex-col w-full h-full">
<div className="flex flex-col w-full h-full" id="onyx-user-dropdown">
<NavigationTab
className="!w-full"
icon={({ className }) => (

View File

@@ -1,17 +1,11 @@
import { test, expect } from "@chromatic-com/playwright";
import { loginAsRandomUser } from "../utils/auth";
import { loginAsRandomUser } from "@tests/e2e/utils/auth";
import {
navigateToAssistantInHistorySidebar,
sendMessage,
startNewChat,
verifyAssistantIsChosen,
} from "../utils/chatActions";
import {
GREETING_MESSAGES,
TOOL_IDS,
waitForUnifiedGreeting,
openActionManagement,
} from "../utils/tools";
} from "@tests/e2e/utils/chatActions";
import { TOOL_IDS, openActionManagement } from "@tests/e2e/utils/tools";
// Tool-related test selectors now imported from shared utils
@@ -26,62 +20,6 @@ test.describe("Default Assistant Tests", () => {
await page.waitForLoadState("networkidle");
});
test.describe("Greeting Message Display", () => {
test("should display greeting message when opening new chat with default assistant", async ({
page,
}) => {
// Look for greeting message - should be one from the predefined list
const greetingText = await waitForUnifiedGreeting(page);
// Verify the greeting is from the predefined list
expect(GREETING_MESSAGES).toContain(greetingText?.trim());
});
test("greeting message should remain consistent during session", async ({
page,
}) => {
// Get initial greeting
const initialGreeting = await waitForUnifiedGreeting(page);
// Reload the page
await page.reload();
await page.waitForLoadState("networkidle");
// Get greeting after reload
const greetingAfterReload = await waitForUnifiedGreeting(page);
// Both greetings should be valid but might differ after reload
expect(GREETING_MESSAGES).toContain(initialGreeting?.trim());
expect(GREETING_MESSAGES).toContain(greetingAfterReload?.trim());
});
test("greeting should only appear for default assistant", async ({
page,
}) => {
// First verify greeting appears for default assistant
const greetingElement = await page.waitForSelector(
'[data-testid="greeting-message"]',
{ timeout: 5000 }
);
expect(greetingElement).toBeTruthy();
// Create a custom assistant to test non-default behavior
await page.getByRole("button", { name: "Explore Assistants" }).click();
await page.getByRole("button", { name: "Create", exact: true }).click();
await page.getByTestId("name").fill("Custom Test Assistant");
await page.getByTestId("description").fill("Test Description");
await page.getByTestId("system_prompt").fill("Test Instructions");
await page.getByRole("button", { name: "Create" }).click();
// Wait for assistant to be created and selected
await verifyAssistantIsChosen(page, "Custom Test Assistant");
// Greeting should NOT appear for custom assistant
const customGreeting = await page.$('[data-testid="greeting-message"]');
expect(customGreeting).toBeNull();
});
});
test.describe("Default Assistant Branding", () => {
test("should display Onyx logo for default assistant", async ({ page }) => {
// Look for Onyx logo

View File

@@ -25,8 +25,10 @@ test("Chat workflow", async ({ page }) => {
// Verify the presence of the expected text
await verifyAssistantIsChosen(page, "Onyx");
await page.pause();
// Test creation of a new assistant
await page.getByRole("button", { name: "Explore Assistants" }).click();
await page.getByTestId("AppSidebar/more-agents").click();
await page.getByRole("button", { name: "Create", exact: true }).click();
await page.getByTestId("name").click();
await page.getByTestId("name").fill("Test Assistant");

View File

@@ -24,7 +24,7 @@ test("LLM Ordering and Model Switching", async ({ page }) => {
// Configure user settings: Set default model to o3 Mini
await page.locator("#onyx-user-dropdown").click();
await page.getByText("User Settings").click();
await page.getByTestId("Settings/user-settings").click();
await page.getByRole("combobox").nth(1).click();
await page.getByLabel("GPT 5", { exact: true }).click();
await page.getByLabel("Close modal").click();

View File

@@ -20,7 +20,9 @@ test.describe("Message Edit and Regenerate Tests", () => {
// Test cancel editing
let userMessage = page.locator("#onyx-human-message").first();
await userMessage.hover();
let editButton = userMessage.locator('[data-testid="edit-button"]').first();
let editButton = userMessage
.locator('[data-testid="HumanMessage/edit-button"]')
.first();
await editButton.click();
let textarea = userMessage.locator("textarea");
@@ -36,7 +38,9 @@ test.describe("Message Edit and Regenerate Tests", () => {
// Edit the message for real
await userMessage.hover();
editButton = userMessage.locator('[data-testid="edit-button"]').first();
editButton = userMessage
.locator('[data-testid="HumanMessage/edit-button"]')
.first();
await editButton.click();
textarea = userMessage.locator("textarea");
@@ -46,10 +50,10 @@ test.describe("Message Edit and Regenerate Tests", () => {
await submitButton.click();
// Wait for the new AI response to complete
await page.waitForSelector('[data-testid="copy-button"]', {
await page.waitForSelector('[data-testid="AIMessage/copy-button"]', {
state: "detached",
});
await page.waitForSelector('[data-testid="copy-button"]', {
await page.waitForSelector('[data-testid="AIMessage/copy-button"]', {
state: "visible",
timeout: 30000,
});
@@ -62,16 +66,16 @@ test.describe("Message Edit and Regenerate Tests", () => {
expect(messageContent).toContain("What is 3+3?");
// Verify version switcher appears and shows 2/2
let messageSwitcher = page.locator('span:has-text("2 / 2")').first();
let messageSwitcher = page.getByTestId("MessageSwitcher/container").first();
await expect(messageSwitcher).toBeVisible();
// Get the parent div that contains the whole switcher
messageSwitcher = messageSwitcher.locator("..").first();
await expect(messageSwitcher).toContainText("2/2");
// Edit again to create a third version
userMessage = page.locator("#onyx-human-message").first();
await userMessage.hover();
editButton = userMessage.locator('[data-testid="edit-button"]').first();
editButton = userMessage
.locator('[data-testid="HumanMessage/edit-button"]')
.first();
await editButton.click();
textarea = userMessage.locator("textarea");
@@ -81,18 +85,19 @@ test.describe("Message Edit and Regenerate Tests", () => {
await submitButton.click();
// Wait for the new AI response to complete
await page.waitForSelector('[data-testid="copy-button"]', {
await page.waitForSelector('[data-testid="AIMessage/copy-button"]', {
state: "detached",
});
await page.waitForSelector('[data-testid="copy-button"]', {
await page.waitForSelector('[data-testid="AIMessage/copy-button"]', {
state: "visible",
timeout: 30000,
});
// Verify navigation between versions
// Find the switcher showing "3 / 3"
let switcherSpan = page.locator('span:has-text("3 / 3")').first();
let switcherSpan = page.getByTestId("MessageSwitcher/container").first();
await expect(switcherSpan).toBeVisible();
await expect(switcherSpan).toContainText("3/3");
// Navigate to previous version - click the first svg icon's parent (left chevron)
await switcherSpan
@@ -103,8 +108,9 @@ test.describe("Message Edit and Regenerate Tests", () => {
.click();
// Check we're now at "2 / 3"
switcherSpan = page.locator('span:has-text("2 / 3")').first();
switcherSpan = page.getByTestId("MessageSwitcher/container").first();
await expect(switcherSpan).toBeVisible({ timeout: 5000 });
await expect(switcherSpan).toContainText("2/3");
// Navigate to first version - re-find the button each time
await switcherSpan
@@ -115,8 +121,9 @@ test.describe("Message Edit and Regenerate Tests", () => {
.click();
// Check we're now at "1 / 3"
switcherSpan = page.locator('span:has-text("1 / 3")').first();
switcherSpan = page.getByTestId("MessageSwitcher/container").first();
await expect(switcherSpan).toBeVisible({ timeout: 5000 });
await expect(switcherSpan).toContainText("1/3");
// Navigate forward using next button - click the last svg icon's parent (right chevron)
await switcherSpan
@@ -127,8 +134,9 @@ test.describe("Message Edit and Regenerate Tests", () => {
.click();
// Check we're back at "2 / 3"
switcherSpan = page.locator('span:has-text("2 / 3")').first();
switcherSpan = page.getByTestId("MessageSwitcher/container").first();
await expect(switcherSpan).toBeVisible({ timeout: 5000 });
await expect(switcherSpan).toContainText("2/3");
});
test("Message regeneration with model selection", async ({ page }) => {
@@ -153,8 +161,8 @@ test.describe("Message Edit and Regenerate Tests", () => {
await aiMessage.hover();
// Click regenerate button using its data-testid
const regenerateButton = aiMessage.locator(
'[data-testid="regenerate-button"]'
const regenerateButton = aiMessage.getByTestId(
"AIMessage/regenerate-button"
);
await regenerateButton.click();
@@ -167,14 +175,17 @@ test.describe("Message Edit and Regenerate Tests", () => {
// Wait for regeneration to complete by waiting for feedback buttons to appear
// The feedback buttons (copy, like, dislike, regenerate) appear when streaming is complete
await page.waitForSelector('[data-testid="regenerate-button"]', {
await page.waitForSelector('[data-testid="AIMessage/regenerate-button"]', {
state: "visible",
timeout: 15000,
});
// Verify version switcher appears showing "2 / 2"
const messageSwitcher = page.locator('span:has-text("2 / 2")').first();
const messageSwitcher = page
.getByTestId("MessageSwitcher/container")
.first();
await expect(messageSwitcher).toBeVisible({ timeout: 5000 });
await expect(messageSwitcher).toContainText("2/2");
// Navigate to previous version
await messageSwitcher
@@ -186,8 +197,9 @@ test.describe("Message Edit and Regenerate Tests", () => {
await page.waitForTimeout(1000);
// Verify we're at "1 / 2"
let switcherSpan = page.locator('span:has-text("1 / 2")').first();
await expect(switcherSpan).toBeVisible();
let switcherSpan = page.getByTestId("MessageSwitcher/container").first();
await expect(switcherSpan).toBeVisible({ timeout: 5000 });
await expect(switcherSpan).toContainText("1/2");
// Verify we're back to the original response
const firstVersionText = await messageContent.textContent();
@@ -203,7 +215,8 @@ test.describe("Message Edit and Regenerate Tests", () => {
await page.waitForTimeout(1000);
// Verify we're back at "2 / 2"
switcherSpan = page.locator('span:has-text("2 / 2")').first();
await expect(switcherSpan).toBeVisible();
switcherSpan = page.getByTestId("MessageSwitcher/container").first();
await expect(switcherSpan).toBeVisible({ timeout: 5000 });
await expect(switcherSpan).toContainText("2/2");
});
});

View File

@@ -33,7 +33,9 @@ export async function sendMessage(page: Page, message: string) {
await page.locator("#onyx-chat-input-send-button").click();
await page.waitForSelector('[data-testid="onyx-ai-message"]');
// Wait for the copy button to appear, which indicates the message is fully rendered
await page.waitForSelector('[data-testid="copy-button"]', { timeout: 30000 });
await page.waitForSelector('[data-testid="AIMessage/copy-button"]', {
timeout: 30000,
});
// Wait for up to 10 seconds for the URL to contain 'chatId='
await page.waitForFunction(
@@ -60,6 +62,6 @@ export async function switchModel(page: Page, modelName: string) {
}
export async function startNewChat(page: Page) {
await page.getByRole("link", { name: "New Chat" }).click();
await expect(page.locator('div[data-testid="chat-intro"]')).toBeVisible();
await page.getByTestId("AppSidebar/new-session").click();
await expect(page.getByTestId("chat-intro")).toBeVisible();
}

View File

@@ -21,7 +21,10 @@
}
],
"paths": {
"@/*": ["./src/*"]
"@/*": ["./src/*"],
"@app/*": ["./src/app/*"],
"@tests/*": ["./tests/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],