Files
AppFlowy-Web/cypress/e2e/chat/chat-input.cy.ts
2025-11-17 13:19:36 +08:00

270 lines
8.5 KiB
TypeScript

import { AuthTestUtils } from '../../support/auth-utils';
import { TestTool } from '../../support/page-utils';
import { AddPageSelectors, ModelSelectorSelectors, PageSelectors, SidebarSelectors, ChatSelectors } from '../../support/selectors';
import { generateRandomEmail, logAppFlowyEnvironment } from '../../support/test-config';
describe('Chat Input Tests', () => {
let testEmail: string;
before(() => {
logAppFlowyEnvironment();
});
beforeEach(() => {
testEmail = generateRandomEmail();
});
it('tests chat input UI controls', () => {
cy.on('uncaught:exception', (err: Error) => {
if (err.message.includes('No workspace or service found') ||
err.message.includes('View not found') ||
err.message.includes('WebSocket') ||
err.message.includes('connection') ||
err.message.includes('Failed to load models') ||
err.message.includes('Minified React error')) {
return false;
}
return true;
});
cy.visit('/login', { failOnStatusCode: false });
cy.wait(2000);
const authUtils = new AuthTestUtils();
authUtils.signInWithTestUrl(testEmail).then(() => {
cy.url().should('include', '/app');
SidebarSelectors.pageHeader().should('be.visible', { timeout: 30000 });
PageSelectors.items().should('exist', { timeout: 30000 });
cy.wait(2000);
TestTool.expandSpace();
cy.wait(1000);
PageSelectors.items()
.first()
.trigger('mouseenter', { force: true })
.trigger('mouseover', { force: true });
cy.wait(1000);
AddPageSelectors.inlineAddButton().first().click({ force: true });
AddPageSelectors.addAIChatButton().should('be.visible').click();
cy.wait(2000);
// Test 1: Format toggle
cy.log('Testing format toggle');
ChatSelectors.formatGroup().then($group => {
if ($group.length > 0) {
ChatSelectors.formatToggle().click();
ChatSelectors.formatGroup().should('not.exist');
}
});
ChatSelectors.formatToggle().should('be.visible').click();
ChatSelectors.formatGroup().should('exist');
ChatSelectors.formatGroup().find('button').should('have.length.at.least', 4);
ChatSelectors.formatToggle().click();
ChatSelectors.formatGroup().should('not.exist');
// Test 2: Model selector
cy.log('Testing model selector');
ModelSelectorSelectors.button().should('be.visible').click();
ModelSelectorSelectors.options().should('exist');
cy.get('body').click(0, 0);
// Test 3: Browse prompts
cy.log('Testing browse prompts');
ChatSelectors.browsePromptsButton().click();
cy.get('[role="dialog"]').should('exist');
cy.get('[role="dialog"]').contains('Browse prompts').should('be.visible');
cy.get('body').type('{esc}');
cy.get('[role="dialog"]').should('not.exist');
// Test 4: Related views
cy.log('Testing related views');
ChatSelectors.relatedViewsButton().click();
ChatSelectors.relatedViewsPopover().should('be.visible');
cy.get('body').type('{esc}');
ChatSelectors.relatedViewsPopover().should('not.exist');
});
});
it('tests chat input message handling', () => {
cy.on('uncaught:exception', (err: Error) => {
if (err.message.includes('No workspace or service found') ||
err.message.includes('View not found') ||
err.message.includes('WebSocket') ||
err.message.includes('connection') ||
err.message.includes('Failed to load models') ||
err.message.includes('Minified React error')) {
return false;
}
return true;
});
cy.visit('/login', { failOnStatusCode: false });
cy.wait(2000);
const authUtils = new AuthTestUtils();
authUtils.signInWithTestUrl(testEmail).then(() => {
cy.url().should('include', '/app');
SidebarSelectors.pageHeader().should('be.visible', { timeout: 30000 });
PageSelectors.items().should('exist', { timeout: 30000 });
cy.wait(2000);
TestTool.expandSpace();
cy.wait(1000);
PageSelectors.items()
.first()
.trigger('mouseenter', { force: true })
.trigger('mouseover', { force: true });
cy.wait(1000);
AddPageSelectors.inlineAddButton().first().click({ force: true });
AddPageSelectors.addAIChatButton().should('be.visible').click();
cy.wait(3000); // Wait for chat to fully load
// Mock API endpoints with more realistic responses
cy.intercept('POST', '**/api/chat/**/message/question', (req) => {
req.reply({
statusCode: 200,
body: {
code: 0,
data: {
message_id: Date.now().toString(),
content: req.body.content || 'Test message',
chat_id: 'test-chat-id',
},
message: 'success',
},
});
}).as('submitQuestion');
cy.intercept('POST', '**/api/chat/**/answer/stream', (req) => {
req.reply({
statusCode: 200,
body: 'data: {"content":"Test response","type":"message"}\n\n',
headers: {
'content-type': 'text/event-stream',
},
});
}).as('streamAnswer');
// Get the textarea using a more flexible selector
const getTextarea = () => cy.get('textarea').first();
// Test 1: Check textarea exists and is ready
cy.log('Testing textarea availability');
getTextarea().should('exist').and('be.visible');
// Test 2: Keyboard interactions with better waits
cy.log('Testing keyboard interactions');
getTextarea()
.should('not.be.disabled')
.clear()
.type('First line')
.should((el) => {
expect(el.val()).to.include('First line');
});
cy.wait(500);
getTextarea()
.type('{shift+enter}Second line')
.should((el) => {
expect(el.val()).to.include('First line\nSecond line');
});
// Test 3: Textarea auto-resize
cy.log('Testing auto-resize');
getTextarea().then($textarea => {
const initialHeight = $textarea.height();
cy.wrap($textarea)
.clear()
.type('Line 1{shift+enter}Line 2{shift+enter}Line 3{shift+enter}Line 4');
cy.wait(500);
getTextarea().then($resized => {
const newHeight = $resized.height();
cy.log(`Initial height: ${initialHeight}, New height: ${newHeight}`);
expect(newHeight).to.be.at.least(initialHeight!);
});
});
// Test 4: Button states with better selectors
cy.log('Testing button states');
getTextarea().clear();
cy.wait(500);
// Check send button is disabled when empty
ChatSelectors.sendButton().should('exist');
ChatSelectors.sendButton().then($button => {
// Button might be disabled via attribute or opacity
const isDisabled = $button.prop('disabled') || $button.css('opacity') === '0.5';
expect(isDisabled).to.be.true;
});
// Type message and check button becomes enabled
getTextarea().type('Test message');
cy.wait(500);
ChatSelectors.sendButton().then($button => {
const isDisabled = $button.prop('disabled') || $button.css('opacity') === '0.5';
expect(isDisabled).to.be.false;
});
// Test 5: Message sending with proper waits
cy.log('Testing message sending');
getTextarea().clear().type('Hello world');
cy.wait(500);
ChatSelectors.sendButton().click();
cy.wait('@submitQuestion', { timeout: 10000 });
// Wait for textarea to be ready again
cy.wait(2000);
getTextarea()
.should('exist')
.and('be.visible')
.and('have.value', '');
// Test 6: Special characters with simpler approach
cy.log('Testing special characters');
cy.wait(1000);
const specialMessage = 'Test with special: @#$%';
getTextarea()
.should('not.be.disabled')
.clear()
.type(specialMessage);
cy.wait(500);
getTextarea().should((el) => {
expect(el.val()).to.equal(specialMessage);
});
// Test 7: Enter sends message with better handling
cy.log('Testing Enter key');
getTextarea().clear();
cy.wait(500);
getTextarea().type('Quick test{enter}');
cy.wait('@submitQuestion', { timeout: 10000 });
cy.wait(2000);
getTextarea()
.should('exist')
.and('have.value', '');
});
});
});