Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { Metadata, Viewport } from 'next'
import { Inter as FontSans, Poppins } from 'next/font/google'
import './globals.css'
import 'katex/dist/katex.min.css';
import 'glassmorphic/glassmorphic.css';
import { cn } from '@/lib/utils'
import { ThemeProvider } from '@/components/theme-provider'
import Header from '@/components/header'
Expand Down
6 changes: 3 additions & 3 deletions components/calendar-notepad.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ export function CalendarNotepad({ chatId }: CalendarNotepadProps) {
};

return (
<div data-testid="calendar-notepad" className="bg-card text-card-foreground shadow-lg rounded-lg p-4 max-w-2xl mx-auto my-4 border">
<div data-testid="calendar-notepad" className="bg-card text-card-foreground shadow-lg rounded-lg p-4 max-w-2xl mx-auto my-4 border overflow-hidden">
<div className="flex items-center justify-between mb-4">
<button
onClick={() => setDateOffset(dateOffset - 7)}
Expand Down Expand Up @@ -151,10 +151,10 @@ export function CalendarNotepad({ chatId }: CalendarNotepadProps) {
<div className="space-y-4">
{notes.length > 0 ? (
notes.map((note) => (
<div key={note.id} className="p-3 bg-muted rounded-md">
<div key={note.id} className="p-3 bg-muted rounded-md overflow-hidden">
<div className="flex justify-between items-start">
<div>
<p className="text-xs text-muted-foreground mb-1">
<p className="text-xs truncate text-muted-foreground mb-1">
{new Date(note.createdAt).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
</p>
<p className="text-sm whitespace-pre-wrap break-words">{note.content}</p>
Expand Down
13 changes: 8 additions & 5 deletions components/chat-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -241,9 +241,7 @@ export const ChatPanel = forwardRef<ChatPanelRef, ChatPanelProps>(({ messages, i
data-testid="chat-input"
className={cn(
'resize-none w-full min-h-12 rounded-fill border border-input pl-14 pr-12 pt-3 pb-1 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
isMobile
? 'mobile-chat-input input bg-background'
: 'bg-muted'
isMobile ? 'mobile-chat-input input bg-background' : 'bg-muted'
)}
onChange={e => {
setInput(e.target.value)
Expand Down Expand Up @@ -292,10 +290,15 @@ export const ChatPanel = forwardRef<ChatPanelRef, ChatPanelProps>(({ messages, i
{selectedFile && (
<div className="w-full px-4 pb-2 mb-2">
<div className="flex items-center justify-between p-2 bg-muted rounded-lg">
<span className="text-sm text-muted-foreground truncate max-w-xs">
<span className="text-sm text-muted-foreground truncate max-w-[60%]">
{selectedFile.name}
</span>
<Button variant="ghost" size="icon" onClick={clearAttachment} data-testid="clear-attachment-button">
<Button
variant="ghost"
size="icon"
onClick={clearAttachment}
data-testid="clear-attachment-button"
>
<X size={16} />
</Button>
</div>
Expand Down
8 changes: 4 additions & 4 deletions components/empty-screen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,21 +34,21 @@ export function EmptyScreen({
return (
<div className={`mx-auto w-full transition-all ${className}`}>
<div className="bg-background p-2">
<div className="mt-4 flex flex-col items-start space-y-2 mb-4">
<div className="mt-4 flex flex-col items-start space-y-2 mb-4 w-full overflow-hidden">
{exampleMessages.map((item) => {
const Icon = item.icon;
return (
<Button
key={item.message} // Use a unique property as the key.
variant="link"
className="h-auto p-0 text-base flex items-center"
className="h-auto p-0 text-base flex items-center w-full overflow-hidden text-left"
name={item.message}
onClick={async () => {
submitMessage(item.message);
}}
>
<Icon size={16} className="mr-2 text-muted-foreground" />
{item.heading}
<Icon size={16} className="mr-2 shrink-0 text-muted-foreground" />
<span className="truncate">{item.heading}</span>
</Button>
);
})}
Expand Down
5 changes: 3 additions & 2 deletions components/search-results-image.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import {
import { useEffect, useState } from 'react'
import { PlusCircle } from 'lucide-react'
import { motion } from 'framer-motion'
import 'glassmorphic/glassmorphic.css'

interface SearchResultsImageSectionProps {
images: string[]
Expand Down Expand Up @@ -98,7 +97,9 @@ export const SearchResultsImageSection: React.FC<
<DialogContent className="sm:max-w-3xl max-h-[80vh] overflow-auto glassmorphic">
<DialogHeader>
<DialogTitle>Search Images</DialogTitle>
<DialogDescription className="text-sm">{query}</DialogDescription>
<DialogDescription className="text-sm line-clamp-2">
{query}
</DialogDescription>
</DialogHeader>
<div className="py-4">
<Carousel
Expand Down
2 changes: 1 addition & 1 deletion components/video-search-results.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ export function VideoSearchResults({ results }: VideoSearchResultsProps) {
<DialogContent className="sm:max-w-3xl max-h-[80vh] overflow-auto">
<DialogHeader>
<DialogTitle>Search Videos</DialogTitle>
<DialogDescription className="text-sm">
<DialogDescription className="text-sm line-clamp-2">
{results.searchParameters.q}
</DialogDescription>
</DialogHeader>
Expand Down
37 changes: 37 additions & 0 deletions tests/responsive.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,43 @@ test.describe('Responsive design - Small Mobile', () => {
expect(messageBox.width).toBeLessThanOrEqual(320);
}
});

test('should not overflow with long attached file name', async ({ page }) => {
const attachmentButton = page.locator('[data-testid="mobile-attachment-button"]');
if (await attachmentButton.isVisible()) {
await page.locator('input[type="file"]').setInputFiles({
name: 'this-is-a-very-long-filename-that-should-not-overflow-the-container-on-small-screens.pdf',
mimeType: 'application/pdf',
buffer: Buffer.from('test')
});

const fileContainer = page.locator('[data-testid="clear-attachment-button"]').locator('..');
if (await fileContainer.isVisible()) {
const containerBox = await fileContainer.boundingBox();
expect(containerBox).toBeTruthy();
if (containerBox) {
expect(containerBox.width).toBeLessThanOrEqual(320);
}

const bodyWidth = await page.evaluate(() => document.body.scrollWidth);
expect(bodyWidth).toBeLessThanOrEqual(321);
}
}
});

test('should not cause horizontal scroll with long query in search dialog', async ({ page }) => {
const dialogDesc = page.locator('[role="dialog"] p.line-clamp-2');
if (await dialogDesc.isVisible()) {
const descBox = await dialogDesc.boundingBox();
expect(descBox).toBeTruthy();
if (descBox) {
expect(descBox.width).toBeLessThanOrEqual(320);
}

const bodyWidth = await page.evaluate(() => document.body.scrollWidth);
expect(bodyWidth).toBeLessThanOrEqual(321);
}
});
Comment on lines +226 to +261

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Make the new small-mobile overflow tests deterministic instead of conditionally skipping.

At Line 228 and Line 251, visibility guards allow both tests to pass without validating anything when elements are absent/closed. Add explicit setup (open dialog/select item, trigger attachment UI) and assert visibility unconditionally before overflow checks.

Suggested tightening
 test('should not overflow with long attached file name', async ({ page }) => {
-  const attachmentButton = page.locator('[data-testid="mobile-attachment-button"]');
-  if (await attachmentButton.isVisible()) {
-    await page.locator('input[type="file"]').setInputFiles({
-      name: 'this-is-a-very-long-filename-that-should-not-overflow-the-container-on-small-screens.pdf',
-      mimeType: 'application/pdf',
-      buffer: Buffer.from('test')
-    });
+  const attachmentButton = page.locator('[data-testid="mobile-attachment-button"]');
+  await expect(attachmentButton).toBeVisible();
+  await page.locator('input[type="file"]').setInputFiles({
+    name: 'this-is-a-very-long-filename-that-should-not-overflow-the-container-on-small-screens.pdf',
+    mimeType: 'application/pdf',
+    buffer: Buffer.from('test')
+  });

-    const fileContainer = page.locator('[data-testid="clear-attachment-button"]').locator('..');
-    if (await fileContainer.isVisible()) {
-      const containerBox = await fileContainer.boundingBox();
-      expect(containerBox).toBeTruthy();
-      if (containerBox) {
-        expect(containerBox.width).toBeLessThanOrEqual(320);
-      }
-
-      const bodyWidth = await page.evaluate(() => document.body.scrollWidth);
-      expect(bodyWidth).toBeLessThanOrEqual(321);
-    }
-  }
+  const fileContainer = page.locator('[data-testid="clear-attachment-button"]').locator('..');
+  await expect(fileContainer).toBeVisible();
+  const containerBox = await fileContainer.boundingBox();
+  expect(containerBox).toBeTruthy();
+  if (containerBox) expect(containerBox.width).toBeLessThanOrEqual(320);
+  const bodyWidth = await page.evaluate(() => document.body.scrollWidth);
+  expect(bodyWidth).toBeLessThanOrEqual(321);
 });

 test('should not cause horizontal scroll with long query in search dialog', async ({ page }) => {
+  // open the search dialog in this test before querying description
+  // (use the existing trigger selector used elsewhere in this suite)
   const dialogDesc = page.locator('[role="dialog"] p.line-clamp-2');
-  if (await dialogDesc.isVisible()) {
-    const descBox = await dialogDesc.boundingBox();
-    expect(descBox).toBeTruthy();
-    if (descBox) {
-      expect(descBox.width).toBeLessThanOrEqual(320);
-    }
-
-    const bodyWidth = await page.evaluate(() => document.body.scrollWidth);
-    expect(bodyWidth).toBeLessThanOrEqual(321);
-  }
+  await expect(dialogDesc).toBeVisible();
+  const descBox = await dialogDesc.boundingBox();
+  expect(descBox).toBeTruthy();
+  if (descBox) expect(descBox.width).toBeLessThanOrEqual(320);
+  const bodyWidth = await page.evaluate(() => document.body.scrollWidth);
+  expect(bodyWidth).toBeLessThanOrEqual(321);
 });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/responsive.spec.ts` around lines 226 - 261, Both tests conditionally
skip their validation logic based on element visibility, which allows them to
pass without actually testing anything. In the test function titled "should not
overflow with long attached file name", add explicit setup to ensure the
attachment UI is triggered and visible before performing the width checks, then
use an expect assertion for visibility instead of the conditional if guard.
Similarly, in the test function titled "should not cause horizontal scroll with
long query in search dialog", add explicit setup to open the search dialog and
display the query text before checking its dimensions, and replace the
conditional visibility check with a direct assertion that the element must be
visible. This makes both tests deterministic by ensuring the responsive overflow
checks always execute and validate the expected UI constraints.

});

test.describe('Responsive design - Large Desktop', () => {
Expand Down