b7f52be4c9
CI / Run CI (push) Has been cancelled
CI / check-backend (push) Has been cancelled
CI / check-frontend (push) Has been cancelled
CI / tests (push) Has been cancelled
CI / e2e-tests (push) Has been cancelled
Copilot Setup Steps / copilot-setup-steps (push) Has been cancelled
458 lines
16 KiB
TypeScript
458 lines
16 KiB
TypeScript
import 'cypress-plugin-steps';
|
|
|
|
describe('Command', () => {
|
|
// Taller viewport reduces header overlap in headless + absolute-positioned menus
|
|
beforeEach(() => {
|
|
cy.viewport(1280, 900);
|
|
});
|
|
|
|
it('should correctly display commands', () => {
|
|
cy.step('Type command shortcut and select Search');
|
|
cy.get(`#chat-input`).type('/sear');
|
|
cy.get('.command-item').should('have.length', 1);
|
|
cy.get('.command-item').eq(0).click();
|
|
|
|
cy.step('Send message with Search command');
|
|
cy.get(`#chat-input`).type('Hello{enter}');
|
|
|
|
cy.step('Verify Search command was applied');
|
|
cy.get('.step').should('have.length', 2);
|
|
cy.get('.step').eq(0).find('.command-span').should('have.text', 'Search');
|
|
|
|
cy.get('#command-button').should('exist');
|
|
|
|
cy.get('.step')
|
|
.eq(1)
|
|
.invoke('text')
|
|
.then((text) => {
|
|
expect(text.trim()).to.equal('Command: Search');
|
|
});
|
|
|
|
cy.step('Select Picture command');
|
|
cy.get(`#chat-input`).type('/pic');
|
|
cy.get('.command-item').should('have.length', 1);
|
|
cy.get('.command-item').eq(0).click();
|
|
|
|
cy.step('Send message with Picture command');
|
|
cy.get(`#chat-input`).type('Hello{enter}');
|
|
|
|
cy.step('Verify Picture command clears all commands');
|
|
cy.get('.step').should('have.length', 4);
|
|
cy.get('.step').eq(2).find('.command-span').should('have.text', 'Picture');
|
|
|
|
// After selecting Picture, backend clears commands -> tools button disappears
|
|
cy.get('#command-button').should('not.exist');
|
|
});
|
|
|
|
it('should correctly display and interact with commands', () => {
|
|
cy.visit('/');
|
|
cy.get('#chat-input').should('exist');
|
|
|
|
cy.step('Verify initial command buttons are visible');
|
|
// Search and StickyButton are button commands
|
|
cy.get('#command-Search').should('exist').and('be.visible');
|
|
cy.get('#command-StickyButton').should('exist').and('be.visible');
|
|
cy.get('#command-Picture').should('not.exist');
|
|
cy.get('#command-Canvas').should('not.exist');
|
|
cy.get('#command-button').should('exist').and('be.visible');
|
|
|
|
cy.step('Open command menu and filter to Picture');
|
|
cy.get('#chat-input').click();
|
|
cy.get('#chat-input').clear();
|
|
cy.get('#chat-input').type('/');
|
|
cy.get('[data-index]').should('have.length.at.least', 3);
|
|
|
|
cy.get('#chat-input').type('pic');
|
|
cy.get('[data-index]').should('have.length', 1);
|
|
cy.get('[data-index="0"]').should('contain', 'Picture');
|
|
cy.get('[data-index="0"]').click();
|
|
|
|
cy.step('Submit message with Picture command');
|
|
cy.get('#chat-input').type('Generate an image{enter}');
|
|
|
|
cy.step('Verify command was sent and UI updated');
|
|
cy.get('.step').should('have.length', 2);
|
|
cy.get('.step').eq(1).should('contain', 'Command: Picture');
|
|
|
|
// Backend cleared commands after Picture -> no tools button and no button pills
|
|
cy.get('#command-Search').should('not.exist');
|
|
cy.get('#command-StickyButton').should('not.exist');
|
|
cy.get('#command-button').should('not.exist');
|
|
});
|
|
|
|
it('should handle keyboard navigation in command menu', () => {
|
|
cy.visit('/');
|
|
cy.get('#chat-input').should('exist');
|
|
|
|
cy.step('Open command menu');
|
|
cy.get('#chat-input').click();
|
|
cy.get('#chat-input').clear();
|
|
cy.get('#chat-input').type('/');
|
|
cy.get('[data-index]').should('have.length.at.least', 3);
|
|
|
|
// Check first item is selected by default using data attribute
|
|
cy.get('[data-index="0"]').should('satisfy', ($el) => {
|
|
// Component adds bg-accent class when selected, but we check for functional state
|
|
return $el.hasClass('bg-accent') || $el.attr('aria-selected') === 'true';
|
|
});
|
|
|
|
cy.step('Navigate down with keyboard');
|
|
cy.get('#chat-input').type('{downArrow}');
|
|
cy.get('[data-index="1"]').should('satisfy', ($el) => {
|
|
return $el.hasClass('bg-accent') || $el.attr('aria-selected') === 'true';
|
|
});
|
|
|
|
cy.step('Navigate up with keyboard');
|
|
cy.get('#chat-input').type('{upArrow}');
|
|
cy.get('[data-index="0"]').should('satisfy', ($el) => {
|
|
return $el.hasClass('bg-accent') || $el.attr('aria-selected') === 'true';
|
|
});
|
|
|
|
cy.step('Select with Enter key');
|
|
cy.get('#chat-input').type('{enter}');
|
|
cy.get('[data-index]').should('not.exist');
|
|
cy.get('[id^="command-"]').should('have.length.at.least', 1);
|
|
});
|
|
|
|
it('should handle Tools dropdown menu', () => {
|
|
cy.visit('/');
|
|
cy.get('#chat-input').should('exist');
|
|
|
|
cy.step('Open Tools dropdown');
|
|
cy.get('#command-button').should('exist').click();
|
|
cy.get('[data-popover-content]').should('be.visible');
|
|
|
|
// Non-button commands are listed: Picture, Canvas, Sticky
|
|
cy.get('[data-popover-content] [data-index]').should(
|
|
'have.length.at.least',
|
|
3
|
|
);
|
|
|
|
cy.step('Verify non-button commands are in dropdown');
|
|
cy.get('[data-popover-content]').should('contain', 'Picture');
|
|
cy.get('[data-popover-content]').should('contain', 'Canvas');
|
|
cy.get('[data-popover-content]').should('contain', 'Sticky');
|
|
|
|
cy.step('Select Canvas from dropdown');
|
|
cy.get('[data-popover-content] [data-index]').contains('Canvas').click();
|
|
cy.get('#command-Canvas').should('exist').and('be.visible');
|
|
cy.get('#command-button').should('exist');
|
|
|
|
cy.step('Send message with Canvas command');
|
|
cy.get('#chat-input').type('Collaborate on code{enter}');
|
|
cy.get('.step').should('contain', 'Command: Canvas');
|
|
});
|
|
|
|
it('should handle button command clicks', () => {
|
|
cy.visit('/');
|
|
cy.get('#chat-input').should('exist');
|
|
|
|
cy.step('Click Search button command');
|
|
cy.get('#command-Search').should('exist');
|
|
cy.get('#command-Search').click();
|
|
|
|
// Check that the button shows selected state (via text color class or aria attribute)
|
|
cy.get('#command-Search').should('satisfy', ($el) => {
|
|
// The component adds text-command class when selected
|
|
return (
|
|
$el.hasClass('text-command') ||
|
|
$el.find('.text-command').length > 0 ||
|
|
$el.attr('aria-pressed') === 'true' ||
|
|
$el.attr('data-selected') === 'true'
|
|
);
|
|
});
|
|
|
|
cy.step('Send message with Search command selected');
|
|
cy.get('#chat-input').type('Search for chainlit{enter}');
|
|
cy.get('.step').should('contain', 'Command: Search');
|
|
cy.get('#command-Search').should('exist');
|
|
|
|
cy.step('Deselect Search command');
|
|
cy.get('#command-Search').click();
|
|
|
|
cy.step('Send message without command');
|
|
cy.get('#chat-input').type('No command message{enter}');
|
|
cy.get('.step').last().should('contain', 'Command:');
|
|
});
|
|
|
|
it('should handle escape key to close command menu', () => {
|
|
cy.visit('/');
|
|
cy.get('#chat-input').should('exist');
|
|
|
|
cy.step('Open command menu');
|
|
cy.get('#chat-input').click();
|
|
cy.get('#chat-input').clear();
|
|
cy.get('#chat-input').type('/');
|
|
cy.get('[data-index]').should('exist');
|
|
cy.get('[data-index]').should('have.length.at.least', 3);
|
|
|
|
cy.step('Close menu with Escape key');
|
|
cy.get('#chat-input').type('{esc}');
|
|
cy.get('[data-index]').should('not.exist');
|
|
cy.get('#chat-input').should('have.value', '/');
|
|
});
|
|
|
|
it('should handle command selection via click', () => {
|
|
cy.visit('/');
|
|
cy.get('#chat-input').should('exist');
|
|
|
|
cy.step('Type partial command');
|
|
cy.get('#chat-input').click();
|
|
cy.get('#chat-input').clear();
|
|
cy.get('#chat-input').type('/can');
|
|
|
|
cy.step('Select Canvas from filtered results');
|
|
cy.get('[data-index]').should('have.length', 1);
|
|
cy.get('[data-index="0"]').should('contain', 'Canvas');
|
|
cy.get('[data-index="0"]').click();
|
|
|
|
cy.step('Verify Canvas command is selected');
|
|
cy.get('#command-Canvas').should('exist').and('be.visible');
|
|
cy.get('[data-index]').should('not.exist');
|
|
cy.get('#chat-input').invoke('val').should('not.contain', '/can');
|
|
});
|
|
|
|
it('should properly handle selected non-button command removal', () => {
|
|
cy.visit('/');
|
|
cy.get('#chat-input').should('exist');
|
|
|
|
cy.step('Select Canvas from Tools dropdown');
|
|
cy.get('#command-button').should('exist').click();
|
|
cy.get('[data-popover-content]').should('be.visible');
|
|
cy.get('[data-popover-content] [data-index]').contains('Canvas').click();
|
|
|
|
cy.step('Verify Canvas is selected');
|
|
cy.get('#command-Canvas').should('exist').and('be.visible');
|
|
|
|
cy.step('Deselect Canvas');
|
|
cy.get('#command-Canvas').click();
|
|
cy.get('#command-Canvas').should('not.exist');
|
|
cy.get('#command-button').should('exist');
|
|
});
|
|
|
|
it('should handle mouse hover in command menus', () => {
|
|
cy.visit('/');
|
|
cy.get('#chat-input').should('exist');
|
|
|
|
cy.step('Open command menu');
|
|
cy.get('#chat-input').click();
|
|
cy.get('#chat-input').clear();
|
|
cy.get('#chat-input').type('/');
|
|
|
|
// Make sure the menu itself is visible
|
|
cy.get('[data-index]').should('have.length.at.least', 3);
|
|
|
|
cy.step('Hover over second item');
|
|
// First item should be selected initially
|
|
cy.get('[data-index="0"]').should('satisfy', ($el) => {
|
|
return $el.hasClass('bg-accent') || $el.attr('aria-selected') === 'true';
|
|
});
|
|
|
|
cy.get('[data-index="1"]').scrollIntoView();
|
|
cy.get('[data-index="1"]').trigger('mousemove', { force: true });
|
|
cy.get('[data-index="1"]').should('satisfy', ($el) => {
|
|
return $el.hasClass('bg-accent') || $el.attr('aria-selected') === 'true';
|
|
});
|
|
|
|
cy.step('Verify selection persists after mouse leave');
|
|
cy.get('[data-index="1"]').trigger('mouseleave', { force: true });
|
|
cy.get('[data-index="1"]').should('satisfy', ($el) => {
|
|
return $el.hasClass('bg-accent') || $el.attr('aria-selected') === 'true';
|
|
});
|
|
});
|
|
|
|
it('should filter commands correctly', () => {
|
|
cy.visit('/');
|
|
cy.get('#chat-input').should('exist');
|
|
|
|
cy.step('Filter for Picture command');
|
|
cy.get('#chat-input').click();
|
|
cy.get('#chat-input').clear();
|
|
cy.get('#chat-input').type('/pic');
|
|
cy.get('[data-index]').should('have.length', 1);
|
|
cy.get('[data-index="0"]').should('contain', 'Picture');
|
|
|
|
cy.step('Filter for Canvas command');
|
|
cy.get('#chat-input').clear();
|
|
cy.get('#chat-input').type('/can');
|
|
cy.get('[data-index]').should('have.length', 1);
|
|
cy.get('[data-index="0"]').should('contain', 'Canvas');
|
|
|
|
cy.step('Filter with non-matching text');
|
|
cy.get('#chat-input').clear();
|
|
cy.get('#chat-input').type('/xyz');
|
|
cy.get('[data-index]').should('not.exist');
|
|
});
|
|
|
|
it('should handle Tools dropdown keyboard navigation', () => {
|
|
cy.visit('/');
|
|
cy.get('#chat-input').should('exist');
|
|
|
|
cy.step('Open Tools dropdown');
|
|
cy.get('#command-button').should('exist').click();
|
|
cy.get('[data-popover-content]').should('be.visible');
|
|
|
|
// First item should be selected by default
|
|
cy.get('[data-popover-content] [data-index="0"]').should(
|
|
'satisfy',
|
|
($el) => {
|
|
return (
|
|
$el.hasClass('bg-accent') || $el.attr('aria-selected') === 'true'
|
|
);
|
|
}
|
|
);
|
|
|
|
cy.step('Navigate down in dropdown');
|
|
cy.get('[data-popover-content]').type('{downArrow}');
|
|
cy.get('[data-popover-content] [data-index="1"]').should(
|
|
'satisfy',
|
|
($el) => {
|
|
return (
|
|
$el.hasClass('bg-accent') || $el.attr('aria-selected') === 'true'
|
|
);
|
|
}
|
|
);
|
|
|
|
cy.step('Navigate up in dropdown');
|
|
cy.get('[data-popover-content]').type('{upArrow}');
|
|
cy.get('[data-popover-content] [data-index="0"]').should(
|
|
'satisfy',
|
|
($el) => {
|
|
return (
|
|
$el.hasClass('bg-accent') || $el.attr('aria-selected') === 'true'
|
|
);
|
|
}
|
|
);
|
|
|
|
cy.step('Select with Enter key');
|
|
cy.get('[data-popover-content]').type('{enter}');
|
|
cy.get('[data-popover-content]').should('not.exist');
|
|
cy.get(
|
|
'[id^="command-"][id$="icture"], [id^="command-"][id$="anvas"], [id^="command-"][id$="ticky"]'
|
|
).should('exist');
|
|
});
|
|
|
|
it('should handle command persistence correctly (non-button)', () => {
|
|
cy.visit('/');
|
|
cy.get('#chat-input').should('exist');
|
|
|
|
cy.step('Select persistent Sticky command');
|
|
cy.get('#chat-input').click();
|
|
cy.get('#chat-input').clear();
|
|
cy.get('#chat-input').type('/sti');
|
|
cy.get('[data-index]').should('have.length.at.least', 1);
|
|
cy.get('[data-index]').contains('Sticky').click();
|
|
|
|
// Selected command should appear as a pill
|
|
cy.get('#command-Sticky').should('exist').and('be.visible');
|
|
|
|
cy.step('Send first message with persistent command');
|
|
cy.get('#chat-input').type('First sticky message{enter}');
|
|
cy.get('.step').last().should('contain', 'Command: Sticky');
|
|
|
|
cy.step('Send second message - command should persist');
|
|
cy.get('#chat-input').type('Second sticky message{enter}');
|
|
cy.get('.step').last().should('contain', 'Command: Sticky');
|
|
|
|
cy.step('Deselect persistent command');
|
|
cy.get('#command-Sticky').click();
|
|
|
|
cy.step('Send message without command');
|
|
cy.get('#chat-input').type('No command now{enter}');
|
|
cy.get('.step').last().should('contain', 'Command:');
|
|
});
|
|
|
|
it('should handle command persistence correctly (button)', () => {
|
|
cy.visit('/');
|
|
cy.get('#chat-input').should('exist');
|
|
|
|
cy.step('Select persistent StickyButton command');
|
|
cy.get('#command-StickyButton').should('exist').click();
|
|
|
|
cy.step('Send first message with persistent button');
|
|
cy.get('#chat-input').type('StickyButton #1{enter}');
|
|
cy.get('.step').last().should('contain', 'Command: StickyButton');
|
|
|
|
cy.step('Send second message - button should remain selected');
|
|
cy.get('#chat-input').type('StickyButton #2{enter}');
|
|
cy.get('.step').last().should('contain', 'Command: StickyButton');
|
|
|
|
cy.step('Deselect persistent button');
|
|
cy.get('#command-StickyButton').click();
|
|
|
|
cy.step('Send message without command');
|
|
cy.get('#chat-input').type('After deselect{enter}');
|
|
cy.get('.step').last().should('contain', 'Command:');
|
|
});
|
|
|
|
it('should show commands in correct places', () => {
|
|
cy.visit('/');
|
|
cy.get('#chat-input').should('exist');
|
|
|
|
cy.step('Verify button commands are visible as buttons');
|
|
// Buttons: Search & StickyButton (button=true) visible; non-button are not
|
|
cy.get('#command-Search').should('exist').and('be.visible');
|
|
cy.get('#command-StickyButton').should('exist').and('be.visible');
|
|
cy.get('#command-Picture').should('not.exist');
|
|
cy.get('#command-Canvas').should('not.exist');
|
|
cy.get('#command-Sticky').should('not.exist');
|
|
|
|
cy.step('Verify Tools menu contains non-button commands');
|
|
cy.get('#command-button').click();
|
|
cy.get('[data-popover-content]').within(() => {
|
|
cy.contains('Picture').should('exist');
|
|
cy.contains('Canvas').should('exist');
|
|
cy.contains('Sticky').should('exist');
|
|
cy.contains('Search').should('not.exist');
|
|
cy.contains('StickyButton').should('not.exist');
|
|
});
|
|
|
|
cy.get('body').click(0, 0);
|
|
cy.get('[data-popover-content]').should('not.exist');
|
|
|
|
cy.step('Verify inline menu contains all commands');
|
|
cy.get('#chat-input').type('/');
|
|
cy.get('[data-index]').should('have.length', 5); // Total: Picture, Search, Canvas, Sticky, StickyButton
|
|
|
|
cy.get('[data-index]')
|
|
.parent()
|
|
.parent()
|
|
.within(() => {
|
|
cy.contains('Picture').should('exist');
|
|
cy.contains('Canvas').should('exist');
|
|
cy.contains('Search').should('exist');
|
|
cy.contains('Sticky').should('exist');
|
|
cy.contains('StickyButton').should('exist');
|
|
});
|
|
});
|
|
|
|
it('should test command clearing behavior with Picture command', () => {
|
|
cy.visit('/');
|
|
cy.get('#chat-input').should('exist');
|
|
|
|
cy.step('Verify initial commands are available');
|
|
cy.get('#command-Search').should('exist');
|
|
cy.get('#command-StickyButton').should('exist');
|
|
cy.get('#command-button').should('exist');
|
|
|
|
cy.step('Select and use Picture command');
|
|
cy.get('#chat-input').type('/pic');
|
|
cy.get('[data-index="0"]').click();
|
|
cy.get('#chat-input').type('Generate a sunset{enter}');
|
|
|
|
cy.step('Verify all commands are cleared after Picture');
|
|
cy.get('#command-Search').should('not.exist');
|
|
cy.get('#command-StickyButton').should('not.exist');
|
|
cy.get('#command-button').should('not.exist');
|
|
|
|
cy.step('Verify slash command shows no commands');
|
|
cy.get('#chat-input').type('/');
|
|
cy.get('[data-index]').should('not.exist');
|
|
|
|
cy.step('Send regular message without commands');
|
|
cy.get('#chat-input').clear();
|
|
cy.get('#chat-input').type('Regular message{enter}');
|
|
cy.get('.step').last().should('contain', 'Command:');
|
|
});
|
|
});
|