LibreChat/e2e/specs/messages.spec.ts
Danny Avila 3c54740074
🪦 refactor: Remove Legacy Code (#10533)
* 🗑️ chore: Remove unused Legacy Provider clients and related helpers

* Deleted OpenAIClient and GoogleClient files along with their associated tests.
* Removed references to these clients in the clients index file.
* Cleaned up typedefs by removing the OpenAISpecClient export.
* Updated chat controllers to use the OpenAI SDK directly instead of the removed client classes.

* chore/remove-openapi-specs

* 🗑️ chore: Remove unused mergeSort and misc utility functions

* Deleted mergeSort.js and misc.js files as they are no longer needed.
* Removed references to cleanUpPrimaryKeyValue in messages.js and adjusted related logic.
* Updated mongoMeili.ts to eliminate local implementations of removed functions.

* chore: remove legacy endpoints

* chore: remove all plugins endpoint related code

* chore: remove unused prompt handling code and clean up imports

* Deleted handleInputs.js and instructions.js files as they are no longer needed.
* Removed references to these files in the prompts index.js.
* Updated docker-compose.yml to simplify reverse proxy configuration.

* chore: remove unused LightningIcon import from Icons.tsx

* chore: clean up translation.json by removing deprecated and unused keys

* chore: update Jest configuration and remove unused mock file

    * Simplified the setupFiles array in jest.config.js by removing the fetchEventSource mock.
    * Deleted the fetchEventSource.js mock file as it is no longer needed.

* fix: simplify endpoint type check in Landing and ConversationStarters components

    * Updated the endpoint type check to use strict equality for better clarity and performance.
    * Ensured consistency in the handling of the azureOpenAI endpoint across both components.

* chore: remove unused dependencies from package.json and package-lock.json

* chore: remove legacy EditController, associated routes and imports

* chore: update banResponse logic to refine request handling for banned users

* chore: remove unused validateEndpoint middleware and its references

* chore: remove unused 'res' parameter from initializeClient in multiple endpoint files

* chore: remove unused 'isSmallScreen' prop from BookmarkNav and NewChat components; clean up imports in ArchivedChatsTable and useSetIndexOptions hooks; enhance localization in PromptVersions

* chore: remove unused import of Constants and TMessage from MobileNav; retain only necessary QueryKeys import

* chore: remove unused TResPlugin type and related references; clean up imports in types and schemas
2025-12-04 14:34:44 -05:00

162 lines
6.2 KiB
TypeScript

import { expect, test } from '@playwright/test';
import type { Response, Page, BrowserContext } from '@playwright/test';
const basePath = 'http://localhost:3080/c/';
const initialUrl = `${basePath}new`;
const endpoints = ['google', 'openAI', 'azureOpenAI'];
const endpoint = endpoints[1];
function isUUID(uuid: string) {
const regex = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
return regex.test(uuid);
}
const waitForServerStream = async (response: Response) => {
const endpointCheck = response.url().includes(`/api/agents`);
return endpointCheck && response.status() === 200;
};
async function clearConvos(page: Page) {
await page.goto(initialUrl, { timeout: 5000 });
await page.getByRole('button', { name: 'test' }).click();
await page.getByText('Settings').click();
await page.getByTestId('clear-convos-initial').click();
await page.getByTestId('clear-convos-confirm').click();
await page.waitForSelector('[data-testid="convo-icon"]', { state: 'detached' });
await page.getByRole('button', { name: 'Close' }).click();
}
let beforeAfterAllContext: BrowserContext;
test.beforeAll(async ({ browser }) => {
console.log('🤖: clearing conversations before message tests.');
beforeAfterAllContext = await browser.newContext();
const page = await beforeAfterAllContext.newPage();
await clearConvos(page);
await page.close();
});
test.beforeEach(async ({ page }) => {
await page.goto(initialUrl, { timeout: 5000 });
});
test.afterEach(async ({ page }) => {
await page.close();
});
test.describe('Messaging suite', () => {
test('textbox should be focused after generation, test expected navigation, & test editing messages', async ({
page,
}) => {
test.setTimeout(120000);
const message = 'hi';
await page.goto(initialUrl, { timeout: 5000 });
await page.locator('#new-conversation-menu').click();
await page.locator(`#${endpoint}`).click();
await page.locator('form').getByRole('textbox').click();
await page.locator('form').getByRole('textbox').fill(message);
const responsePromise = [
page.waitForResponse(waitForServerStream),
page.locator('form').getByRole('textbox').press('Enter'),
];
const [response] = (await Promise.all(responsePromise)) as [Response];
const responseBody = await response.body();
const messageSuccess = responseBody.includes('"final":true');
expect(messageSuccess).toBe(true);
// Check if textbox is focused
await page.waitForTimeout(250);
const isTextboxFocused = await page.evaluate(() => {
return document.activeElement === document.querySelector('[data-testid="text-input"]');
});
expect(isTextboxFocused).toBeTruthy();
const currentUrl = page.url();
expect(currentUrl).toBe(initialUrl);
//cleanup the conversation
await page.getByTestId('nav-new-chat-button').click();
expect(page.url()).toBe(initialUrl);
// Click on the first conversation
await page.getByTestId('convo-icon').first().click({ timeout: 5000 });
const finalUrl = page.url();
const conversationId = finalUrl.split(basePath).pop() ?? '';
expect(isUUID(conversationId)).toBeTruthy();
// Check if editing works
const editText = 'All work and no play makes Johnny a poor boy';
await page.getByRole('button', { name: 'edit' }).click();
const textEditor = page.getByTestId('message-text-editor');
await textEditor.click();
await textEditor.fill(editText);
await page.getByRole('button', { name: 'Save', exact: true }).click();
const updatedTextElement = page.getByText(editText);
expect(updatedTextElement).toBeTruthy();
// Check edit response
await page.getByRole('button', { name: 'edit' }).click();
const editResponsePromise = [
page.waitForResponse(waitForServerStream),
await page.getByRole('button', { name: 'Save & Submit' }).click(),
];
const [editResponse] = (await Promise.all(editResponsePromise)) as [Response];
const editResponseBody = await editResponse.body();
const editSuccess = editResponseBody.includes('"final":true');
expect(editSuccess).toBe(true);
// The generated message should include the edited text
const currentTextContent = await updatedTextElement.innerText();
expect(currentTextContent.includes(editText)).toBeTruthy();
});
test('message should stop and continue', async ({ page }) => {
const message = 'write me a 10 stanza poem about space';
await page.goto(initialUrl, { timeout: 5000 });
await page.locator('#new-conversation-menu').click();
await page.locator(`#${endpoint}`).click();
await page.click('button[data-testid="select-dropdown-button"]:has-text("Model:")');
await page.getByRole('option', { name: 'gpt-3.5-turbo', exact: true }).click();
await page.locator('form').getByRole('textbox').click();
await page.locator('form').getByRole('textbox').fill(message);
let responsePromise = [
page.waitForResponse(waitForServerStream),
page.locator('form').getByRole('textbox').press('Enter'),
];
(await Promise.all(responsePromise)) as [Response];
// Wait for first Partial tick (it takes 500 ms for server to save the current message stream)
await page.waitForTimeout(250);
await page.getByRole('button', { name: 'Stop' }).click();
responsePromise = [
page.waitForResponse(waitForServerStream),
page.getByTestId('continue-generation-button').click(),
];
(await Promise.all(responsePromise)) as [Response];
const regenerateButton = page.getByRole('button', { name: 'Regenerate' });
expect(regenerateButton).toBeTruthy();
// Clear conversation since it seems to persist despite other tests clearing it
await page.getByTestId('convo-item').getByRole('button').nth(1).click();
});
// in this spec as we are testing post-message navigation, we are not testing the message response
test('Page navigations', async ({ page }) => {
await page.goto(initialUrl, { timeout: 5000 });
await page.getByTestId('convo-icon').first().click({ timeout: 5000 });
const currentUrl = page.url();
const conversationId = currentUrl.split(basePath).pop() ?? '';
expect(isUUID(conversationId)).toBeTruthy();
await page.getByTestId('nav-new-chat-button').click();
expect(page.url()).toBe(initialUrl);
});
});