Skip to content

Commit 41185a5

Browse files
committed
Add first e2e test; Add e2e section in the README.md file; implement Chat PO and commands
Signed-off-by: Francesco Torchia <[email protected]>
1 parent ad16791 commit 41185a5

File tree

14 files changed

+191
-12
lines changed

14 files changed

+191
-12
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,3 +107,6 @@ shell/rancher-components
107107
scripts/standalone/ui
108108
scripts/standalone/cert
109109
scripts/standalone/node
110+
111+
# Cypress
112+
browser-logs

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,18 @@ Bump the app version on `package.json` file, then run:
3434
yarn serve-pkgs
3535
```
3636

37+
## E2E tests
38+
Running locally:
39+
```bash
40+
# Build and Run the mock Agent
41+
cd mock-agent
42+
yarn install
43+
yarn mock:agent:start
44+
45+
# Launch Cypress dashboard
46+
API=https://your-rancher VUE_APP_AGENT_MESSAGES_WS_PATH=ws://localhost:8000/ws/agent yarn dev
47+
```
48+
3749
License
3850
=======
3951
Check Rancher AI UI Apache License details [here](LICENSE)

cypress.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ export default defineConfig({
162162
VAI_ENABLED: process.env.VAI_ENABLED,
163163
accessibility: !!process.env.TEST_A11Y, // Are we running accessibility tests?
164164
a11yFolder: path.join('.', 'cypress', 'accessibility'),
165+
mockAgentApi: 'http://localhost:8000',
165166
},
166167
e2e: {
167168
fixturesFolder: 'cypress/e2e/blueprints',
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import ComponentPo from '@/cypress/e2e/po/components/component.po';
2+
3+
export default class ChatPo extends ComponentPo {
4+
constructor() {
5+
super('[data-testid="rancher-ai-ui-chat-container"]');
6+
}
7+
8+
checkExists(): Cypress.Chainable<boolean> {
9+
return this.self().should('exist');
10+
}
11+
12+
messages() {
13+
return this.self().get('[data-testid="rancher-ai-ui-chat-message"]');
14+
}
15+
16+
getMessage(index: number) {
17+
return this.messages().eq(index);
18+
}
19+
20+
getTemplateMessage(templateName: string) {
21+
return this.self().get(`[data-testid="rancher-ai-ui-chat-message-template-${ templateName }"]`);
22+
}
23+
}

cypress/e2e/po/extensions/ai/chat.ts

Whitespace-only changes.

cypress/e2e/tests/chat.spec.ts

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import { HeaderPo } from '@/cypress/e2e/po/components/header.po';
2+
import HomePagePo from '@/cypress/e2e/po/pages/home.po';
3+
import ClusterDashboardPagePo from '@/cypress/e2e/po/pages/explorer/cluster-dashboard.po';
4+
import ClusterManagerListPagePo from '@/cypress/e2e/po/pages/cluster-manager/cluster-manager-list.po';
5+
import { FleetDashboardListPagePo } from '@/cypress/e2e/po/pages/fleet/fleet-dashboard.po';
6+
import UsersPo from '@/cypress/e2e/po/pages/users-and-auth/users.po';
7+
import ExtensionsPagePo from '@/cypress/e2e/po/pages/extensions.po';
8+
import { SettingsPagePo } from '@/cypress/e2e/po/pages/global-settings/settings.po';
9+
10+
import ChatPo from '@/cypress/e2e/po/extensions/ai/chat.po';
11+
12+
describe('Chat', () => {
13+
const header = new HeaderPo();
14+
const chat = new ChatPo();
15+
16+
beforeEach(() => {
17+
cy.login();
18+
});
19+
20+
describe('Open Chat from different UI areas', () => {
21+
it('Home', () => {
22+
HomePagePo.goTo();
23+
24+
header.askLizButton().click();
25+
chat.checkExists();
26+
});
27+
28+
it('Cluster dashboard', () => {
29+
const localClusterDashboard = new ClusterDashboardPagePo('local');
30+
31+
localClusterDashboard.goTo();
32+
33+
header.askLizButton().click();
34+
chat.checkExists();
35+
});
36+
37+
it('Cluster management', () => {
38+
const clusterList = new ClusterManagerListPagePo();
39+
40+
clusterList.goTo();
41+
clusterList.waitForPage();
42+
43+
header.askLizButton().click();
44+
chat.checkExists();
45+
});
46+
47+
it('Fleet', () => {
48+
const fleetDashboardPage = new FleetDashboardListPagePo('_');
49+
50+
fleetDashboardPage.goTo();
51+
fleetDashboardPage.waitForPage();
52+
53+
header.askLizButton().click();
54+
chat.checkExists();
55+
});
56+
57+
it('Users', () => {
58+
const usersPo = new UsersPo();
59+
60+
usersPo.goTo();
61+
usersPo.waitForPage();
62+
63+
header.askLizButton().click();
64+
chat.checkExists();
65+
});
66+
67+
it('Extensions', () => {
68+
const extensionsPo = new ExtensionsPagePo();
69+
70+
extensionsPo.goTo();
71+
extensionsPo.waitForPage();
72+
73+
header.askLizButton().click();
74+
chat.checkExists();
75+
});
76+
77+
it('Settings', () => {
78+
const settingsPage = new SettingsPagePo('_');
79+
80+
settingsPage.goTo();
81+
settingsPage.waitForPage();
82+
83+
header.askLizButton().click();
84+
chat.checkExists();
85+
});
86+
});
87+
88+
describe('Welcome Message', () => {
89+
it('Show welcome message template and suggestions', () => {
90+
cy.login();
91+
92+
HomePagePo.goTo();
93+
94+
cy.enqueueAIAgentResponse({
95+
content: 'Providing mock response from the agent, containing suggestions for the welcome message. <suggestion>View resources</suggestion><suggestion>Analyze logs</suggestion><suggestion>Do action</suggestion>',
96+
chunkSize: 30
97+
});
98+
99+
header.askLizButton().click();
100+
chat.checkExists();
101+
102+
const welcomeMessage = chat.getTemplateMessage('welcome');
103+
104+
welcomeMessage.should('exist');
105+
welcomeMessage.within(() => {
106+
cy.contains("I'm Liz, your personal AI assistant. How can I help you?").should('be.visible');
107+
108+
const suggestions = ['View resources', 'Analyze logs', 'Do action'];
109+
110+
suggestions.forEach((s) => {
111+
cy.contains(s, { timeout: 5000 }).should('be.visible');
112+
});
113+
});
114+
});
115+
});
116+
});

cypress/globals.d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,10 @@ declare global {
232232
*/
233233
checkElementAccessibility(selector: any, description?: string);
234234

235+
/**
236+
* enqueue a request to mock AI Agent response
237+
*/
238+
enqueueAIAgentResponse(args: { content: string, chunkSize?: number }): Chainable;
235239
}
236240
}
237241
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
2+
/**
3+
* Enqueue a request to mock AI Agent response
4+
*/
5+
Cypress.Commands.add('enqueueAIAgentResponse', (args: { content: string, chunkSize?: number }) => {
6+
return cy.request({
7+
method: 'POST',
8+
url: `${ Cypress.env('mockAgentApi') }/control/enqueue`,
9+
body: {
10+
content: args.content || '',
11+
chunkSize: args.chunkSize || 30
12+
}
13+
})
14+
.then((resp) => {
15+
expect(resp.status).to.eq(200);
16+
});
17+
});

cypress/support/e2e.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import './commands/commands';
33
import './commands/chainable';
44
import './commands/rancher-api-commands';
55
import './commands/accessiblity';
6+
import './commands/mock-ai-agent-api';
67

78
import registerCypressGrep from '@cypress/grep/src/support';
89
import { addCustomCommand } from 'cypress-delete-downloads-folder';

pkg/rancher-ai-ui/components/message/index.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ onBeforeUnmount(() => {
105105
'chat-message-user': props.message.role === RoleEnum.User,
106106
disabled: props.disabled
107107
}"
108+
:data-testid="`rancher-ai-ui-chat-message-${ props.message.id }`"
108109
>
109110
<component
110111
:is="props.message.role === RoleEnum.User ? UserAvatar : SystemAvatar"

0 commit comments

Comments
 (0)