LibreChat/e2e/specs/mock/db.ts
Danny Avila 186b738d2d
Some checks failed
Docker Dev Branch Images Build / build (Dockerfile, lc-dev, node) (push) Has been cancelled
Docker Dev Branch Images Build / build (Dockerfile.multi, lc-dev-api, api-build) (push) Has been cancelled
GitNexus Index / index (push) Has been cancelled
GitNexus Index / post-index (push) Has been cancelled
🪟 fix: Re-measure Sidebar Chat List on Width Change to Fix Date-Group Spacing (#13981)
* 🪟 fix: Re-measure sidebar chat list on width change to fix date-group spacing

When the sidebar is expanded from a collapsed reload, virtualized rows first
measure mid-animation at a narrow width, so date-group headers wrap and cache an
inflated height. CellMeasurerCache(fixedWidth) keys heights by row, not width, so
the stale height persists once full width is reached — leaving gaps under headers.

Invalidate the measurement cache and recompute row heights whenever the measured
list width changes. Adds a Playwright mock e2e (seeds backdated convos across date
groups via a new db helper) that fails without the fix and passes with it.

* 🧪 test: Harden sidebar e2e (runtime-env path, midnight-safe seed, convo isolation)

Addresses Codex review on PR #13981:
- db.ts honors E2E_RUNTIME_ENV_PATH when locating the runtime Mongo URI.
- Seed timestamps anchor on local noon so the Today group stays in-day near midnight.
- Clear the shared user's conversations before seeding so later date-group headers
  are not pushed below the virtualized viewport by other specs' leftover chats.
2026-06-26 13:43:03 -04:00

91 lines
2.9 KiB
TypeScript

import fs from 'fs';
import path from 'path';
import { MongoClient } from 'mongodb';
import type { Db } from 'mongodb';
const DEFAULT_MONGO_URI = 'mongodb://127.0.0.1:27017/LibreChat-e2e';
/**
* e2e/setup/start-server.js writes the active Mongo URI (memory-Mongo port included)
* here on boot. It honors `E2E_RUNTIME_ENV_PATH`, so resolve the same override the
* server used before falling back to the default location.
*/
function getRuntimeEnvPath(): string {
return (
process.env.E2E_RUNTIME_ENV_PATH ?? path.resolve(__dirname, '../.test-results/runtime-env.json')
);
}
function getMongoUri(): string {
try {
const env = JSON.parse(fs.readFileSync(getRuntimeEnvPath(), 'utf8')) as { MONGO_URI?: string };
if (env.MONGO_URI) {
return env.MONGO_URI;
}
} catch {
/* fall through to env/default */
}
return process.env.MONGO_URI ?? DEFAULT_MONGO_URI;
}
async function resolveUserId(db: Db, userEmail: string): Promise<string> {
const user = await db.collection('users').findOne({ email: userEmail });
if (!user) {
throw new Error(`E2E seed: user "${userEmail}" not found`);
}
return user._id.toString();
}
/** Connect to the e2e MongoDB, run `fn`, and always close the client. */
export async function withMongo<T>(fn: (db: Db) => Promise<T>): Promise<T> {
const client = new MongoClient(getMongoUri());
await client.connect();
try {
return await fn(client.db());
} finally {
await client.close();
}
}
export interface SeedConvo {
conversationId: string;
title: string;
/** Drives the sidebar date group ("Today", "Previous 7 days", ...). */
updatedAt: Date;
}
/**
* Inserts conversation documents directly (bypassing mongoose timestamps) so their
* `updatedAt` can be backdated into specific sidebar date groups.
*/
export async function seedConversations(userEmail: string, convos: SeedConvo[]): Promise<void> {
await withMongo(async (db) => {
const userId = await resolveUserId(db, userEmail);
const docs = convos.map((convo) => ({
conversationId: convo.conversationId,
title: convo.title,
user: userId,
endpoint: 'openAI',
isArchived: false,
createdAt: convo.updatedAt,
updatedAt: convo.updatedAt,
__v: 0,
}));
await db.collection('conversations').insertMany(docs);
});
}
export async function deleteConversations(conversationIds: string[]): Promise<void> {
await withMongo(async (db) => {
await db.collection('conversations').deleteMany({ conversationId: { $in: conversationIds } });
});
}
/** Clears every conversation for the user so the seeded date groups are not pushed
* below the virtualized viewport by rows left behind by other specs. */
export async function clearUserConversations(userEmail: string): Promise<void> {
await withMongo(async (db) => {
const userId = await resolveUserId(db, userEmail);
await db.collection('conversations').deleteMany({ user: userId });
});
}