Skip to content

Commit 0493d85

Browse files
asvishnyakovCursor Agent
andauthored
test(cypress): stabilize, lint & format all tests to prepare for Chainlit#2573 (Chainlit#2877)
## PR summary ### What Brings the **`cypress/`** tree in line with **`feat/refactor-scripts`**: same files and content as on that branch, split into focused commits for review. ### Why - **Ruff** — sample `main.py` apps under `cypress/e2e/` follow current lint/import rules. - **Prettier** — formatting for JSX fixtures, theme JSON, markdown, iframe HTML, support TS, and affected specs. - **eslint-plugin-cypress** — safer patterns in specs (no unsafe chains, clearer assertions). - **Stability** — copilot e2e, `window_message` / `llama_index_cb`, and `starters_categories` (fewer arbitrary `cy.wait`, visibility before clicks). <!-- This is an auto-generated description by cubic. --> --- ## Summary by cubic Stabilizes and cleans up the Cypress E2E suite to reduce flakiness and align with the scripts refactor, preparing for Chainlit#2573. Applies `Prettier` formatting, `Ruff` import rules, and `eslint-plugin-cypress` safe patterns across tests and sample apps. - **Bug Fixes** - Window messaging: add on-chat-start readiness message, assert inside the iframe, and send `postMessage` without sleeps. - Copilot sidebar: deterministic thread ID checks via updated `getCopilotThreadId`, drag-resize/margin assertions with `cy.document().should`, no arbitrary waits. - Replace brittle `cy.wait` with visibility/assertion-driven flows across starters, chat profiles, command/elements/streaming, upload attachments, and sidebar close. - **Refactors** - Run `Prettier` on JSX fixtures (`JiraTicket.jsx`, `Counter.jsx`), theme JSON, iframe HTML, markdown, and affected specs/support TS. - Align sample `main.py` apps with `Ruff` import order and spacing. - Enforce `eslint-plugin-cypress` patterns: split chained commands, use explicit `.should` assertions; test utils add optional assertion to `getCopilotThreadId`. <sup>Written for commit 42f83d7. Summary will update on new commits.</sup> <!-- End of auto-generated description by cubic. --> --------- Co-authored-by: Cursor Agent <noreply@cursor.com>
1 parent 9ce0b7e commit 0493d85

31 files changed

Lines changed: 279 additions & 191 deletions

File tree

cypress/e2e/ask_custom_element/public/elements/JiraTicket.jsx

Lines changed: 66 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,25 @@
1-
import { Button } from "@/components/ui/button";
2-
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
3-
import { Input } from "@/components/ui/input";
4-
import { Label } from "@/components/ui/label";
5-
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
6-
import { Textarea } from "@/components/ui/textarea";
71
import React, { useEffect, useMemo, useState } from 'react';
82

3+
import { Button } from '@/components/ui/button';
4+
import {
5+
Card,
6+
CardContent,
7+
CardDescription,
8+
CardFooter,
9+
CardHeader,
10+
CardTitle
11+
} from '@/components/ui/card';
12+
import { Input } from '@/components/ui/input';
13+
import { Label } from '@/components/ui/label';
14+
import {
15+
Select,
16+
SelectContent,
17+
SelectItem,
18+
SelectTrigger,
19+
SelectValue
20+
} from '@/components/ui/select';
21+
import { Textarea } from '@/components/ui/textarea';
22+
923
export default function JiraTicket() {
1024
const [timeLeft, setTimeLeft] = useState(props.timeout || 30);
1125
const [values, setValues] = useState(() => {
@@ -40,10 +54,19 @@ export default function JiraTicket() {
4054
const value = values[field.id];
4155
switch (field.type) {
4256
case 'textarea':
43-
return <Textarea id={field.id} value={value} onChange={(e) => handleChange(field.id, e.target.value)} />;
57+
return (
58+
<Textarea
59+
id={field.id}
60+
value={value}
61+
onChange={(e) => handleChange(field.id, e.target.value)}
62+
/>
63+
);
4464
case 'select':
4565
return (
46-
<Select value={value} onValueChange={(val) => handleChange(field.id, val)}>
66+
<Select
67+
value={value}
68+
onValueChange={(val) => handleChange(field.id, val)}
69+
>
4770
<SelectTrigger id={field.id}>
4871
<SelectValue placeholder={field.label} />
4972
</SelectTrigger>
@@ -57,19 +80,44 @@ export default function JiraTicket() {
5780
</Select>
5881
);
5982
case 'date':
60-
return <Input type="date" id={field.id} value={value} onChange={(e) => handleChange(field.id, e.target.value)} />;
83+
return (
84+
<Input
85+
type="date"
86+
id={field.id}
87+
value={value}
88+
onChange={(e) => handleChange(field.id, e.target.value)}
89+
/>
90+
);
6191
case 'datetime':
62-
return <Input type="datetime-local" id={field.id} value={value} onChange={(e) => handleChange(field.id, e.target.value)} />;
92+
return (
93+
<Input
94+
type="datetime-local"
95+
id={field.id}
96+
value={value}
97+
onChange={(e) => handleChange(field.id, e.target.value)}
98+
/>
99+
);
63100
default:
64-
return <Input id={field.id} value={value} onChange={(e) => handleChange(field.id, e.target.value)} />;
101+
return (
102+
<Input
103+
id={field.id}
104+
value={value}
105+
onChange={(e) => handleChange(field.id, e.target.value)}
106+
/>
107+
);
65108
}
66109
};
67110

68111
return (
69-
<Card id="jira-ticket" className="mt-4 w-full max-w-3xl grid grid-cols-2 gap-4">
112+
<Card
113+
id="jira-ticket"
114+
className="mt-4 w-full max-w-3xl grid grid-cols-2 gap-4"
115+
>
70116
<CardHeader className="col-span-2">
71117
<CardTitle>Create JIRA Ticket</CardTitle>
72-
<CardDescription>Provide details for the new issue. {timeLeft}s left</CardDescription>
118+
<CardDescription>
119+
Provide details for the new issue. {timeLeft}s left
120+
</CardDescription>
73121
</CardHeader>
74122
<CardContent className="col-span-2 grid grid-cols-2 gap-4">
75123
{props.fields.map((field) => (
@@ -83,7 +131,11 @@ export default function JiraTicket() {
83131
))}
84132
</CardContent>
85133
<CardFooter className="col-span-2 flex justify-end gap-2">
86-
<Button id="ticket-cancel" variant="outline" onClick={() => cancelElement()}>
134+
<Button
135+
id="ticket-cancel"
136+
variant="outline"
137+
onClick={() => cancelElement()}
138+
>
87139
Cancel
88140
</Button>
89141
<Button

cypress/e2e/ask_file/main.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import aiofiles
2-
32
import chainlit as cl
43

54

cypress/e2e/auth/spec.cy.ts

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,24 @@
1-
import { loadCopilotScript, mountCopilotWidget, openCopilot, submitMessage } from '../../support/testUtils';
1+
import {
2+
loadCopilotScript,
3+
mountCopilotWidget,
4+
openCopilot,
5+
submitMessage
6+
} from '../../support/testUtils';
27

38
function login() {
49
return cy.request({
510
method: 'GET',
611
url: '/auth/custom',
712
followRedirect: false
8-
})
13+
});
914
}
1015

1116
function getToken() {
1217
return cy.request({
1318
method: 'GET',
1419
url: '/auth/token',
1520
followRedirect: false
16-
})
21+
});
1722
}
1823

1924
function shouldShowGreetingMessage() {
@@ -26,14 +31,13 @@ function shouldShowGreetingMessage() {
2631
function shouldSendMessageAndRecieveAnswer() {
2732
it('should send message and receive answer', () => {
2833
cy.get('.step').should('contain', 'Hello');
29-
34+
3035
const testMessage = 'Test message from custom auth';
3136
submitMessage(testMessage);
3237

3338
cy.get('.step').should('contain', 'Echo:');
3439
cy.get('.step').should('contain', testMessage);
3540
});
36-
3741
}
3842

3943
describe('Custom Auth', () => {
@@ -106,7 +110,10 @@ describe('Copilot Token', { includeShadowDom: true }, () => {
106110
it('should throw error about missing authentication token', () => {
107111
mountCopilotWidget();
108112
openCopilot();
109-
cy.get('#chainlit-copilot-chat').should('contain', 'No authentication token provided.');
113+
cy.get('#chainlit-copilot-chat').should(
114+
'contain',
115+
'No authentication token provided.'
116+
);
110117
});
111118
});
112119

@@ -115,13 +122,13 @@ describe('Copilot Token', { includeShadowDom: true }, () => {
115122
getToken().then((response) => {
116123
expect(response.status).to.equal(200);
117124

118-
const accessToken = response.body
125+
const accessToken = response.body;
119126
expect(accessToken).to.not.be.null;
120127

121128
mountCopilotWidget({ accessToken });
122129
openCopilot();
123130
});
124-
})
131+
});
125132

126133
shouldShowGreetingMessage();
127134

cypress/e2e/chat_profiles/spec.cy.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ describe('Chat profiles', () => {
88
cy.get("button[type='submit']").click();
99
cy.get('#chat-input').should('exist');
1010

11-
cy.wait(1000);
12-
cy.get('#starter-say-hi').should('exist').click();
11+
cy.get('#starter-say-hi').should('be.visible').click();
1312

1413
cy.get('.step')
1514
.should('have.length', 2)
@@ -33,9 +32,10 @@ describe('Chat profiles', () => {
3332
cy.get('[data-test="select-item:GPT-4"]').click();
3433
cy.get('#confirm').click();
3534

36-
cy.wait(1000);
37-
38-
cy.get('#starter-ask-for-help').should('not.be.disabled').click();
35+
cy.get('#starter-ask-for-help')
36+
.should('be.visible')
37+
.should('not.be.disabled')
38+
.click();
3939

4040
cy.get('.step')
4141
.should('have.length', 2)
@@ -109,7 +109,7 @@ describe('Chat profiles', () => {
109109
// Select GPT-4 profile
110110
cy.get('[data-test="select-item:GPT-4"]').click();
111111

112-
cy.wait(1000);
112+
cy.get('#chat-input').should('be.visible');
113113

114114
// Verify the profile has been changed
115115
submitMessage('hello');

cypress/e2e/chat_settings/spec.cy.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,26 @@ describe('Customize chat settings', () => {
1616
cy.contains('gpt-4').click();
1717
cy.get('#Model').should('contain.text', 'gpt-4');
1818

19+
cy.get('#Temperature').find('[role="slider"]').focus();
20+
cy.get('#Temperature')
21+
.find('[role="slider"]')
22+
.type('{leftarrow}'.repeat(6));
1923
cy.get('#Temperature')
2024
.find('[role="slider"]')
21-
.focus()
22-
.type('{leftarrow}'.repeat(6))
2325
.should('have.attr', 'aria-valuenow', '0.4');
2426

27+
cy.get('#SAI_Steps').find('[role="slider"]').focus();
28+
cy.get('#SAI_Steps').find('[role="slider"]').type('{rightarrow}'.repeat(6));
2529
cy.get('#SAI_Steps')
2630
.find('[role="slider"]')
27-
.focus()
28-
.type('{rightarrow}'.repeat(6))
2931
.should('have.attr', 'aria-valuenow', '36');
3032

33+
cy.get('#SAI_Cfg_Scale').find('[role="slider"]').focus();
34+
cy.get('#SAI_Cfg_Scale')
35+
.find('[role="slider"]')
36+
.type('{rightarrow}'.repeat(6));
3137
cy.get('#SAI_Cfg_Scale')
3238
.find('[role="slider"]')
33-
.focus()
34-
.type('{rightarrow}'.repeat(6))
3539
.should('have.attr', 'aria-valuenow', '7.6');
3640

3741
cy.contains('Confirm').click();

cypress/e2e/command/spec.cy.ts

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ describe('Command', () => {
5757
cy.get('#command-button').should('exist').and('be.visible');
5858

5959
cy.step('Open command menu and filter to Picture');
60-
cy.get('#chat-input').click().clear();
60+
cy.get('#chat-input').click();
61+
cy.get('#chat-input').clear();
6162
cy.get('#chat-input').type('/');
6263
cy.get('[data-index]').should('have.length.at.least', 3);
6364

@@ -84,7 +85,8 @@ describe('Command', () => {
8485
cy.get('#chat-input').should('exist');
8586

8687
cy.step('Open command menu');
87-
cy.get('#chat-input').click().clear();
88+
cy.get('#chat-input').click();
89+
cy.get('#chat-input').clear();
8890
cy.get('#chat-input').type('/');
8991
cy.get('[data-index]').should('have.length.at.least', 3);
9092

@@ -178,9 +180,11 @@ describe('Command', () => {
178180
cy.get('#chat-input').should('exist');
179181

180182
cy.step('Open command menu');
181-
cy.get('#chat-input').click().clear();
183+
cy.get('#chat-input').click();
184+
cy.get('#chat-input').clear();
182185
cy.get('#chat-input').type('/');
183-
cy.get('[data-index]').should('exist').and('have.length.at.least', 3);
186+
cy.get('[data-index]').should('exist');
187+
cy.get('[data-index]').should('have.length.at.least', 3);
184188

185189
cy.step('Close menu with Escape key');
186190
cy.get('#chat-input').type('{esc}');
@@ -193,7 +197,8 @@ describe('Command', () => {
193197
cy.get('#chat-input').should('exist');
194198

195199
cy.step('Type partial command');
196-
cy.get('#chat-input').click().clear();
200+
cy.get('#chat-input').click();
201+
cy.get('#chat-input').clear();
197202
cy.get('#chat-input').type('/can');
198203

199204
cy.step('Select Canvas from filtered results');
@@ -230,7 +235,8 @@ describe('Command', () => {
230235
cy.get('#chat-input').should('exist');
231236

232237
cy.step('Open command menu');
233-
cy.get('#chat-input').click().clear();
238+
cy.get('#chat-input').click();
239+
cy.get('#chat-input').clear();
234240
cy.get('#chat-input').type('/');
235241

236242
// Make sure the menu itself is visible
@@ -242,16 +248,14 @@ describe('Command', () => {
242248
return $el.hasClass('bg-accent') || $el.attr('aria-selected') === 'true';
243249
});
244250

245-
cy.get('[data-index="1"]')
246-
.scrollIntoView()
247-
.trigger('mousemove', { force: true });
251+
cy.get('[data-index="1"]').scrollIntoView();
252+
cy.get('[data-index="1"]').trigger('mousemove', { force: true });
248253
cy.get('[data-index="1"]').should('satisfy', ($el) => {
249254
return $el.hasClass('bg-accent') || $el.attr('aria-selected') === 'true';
250255
});
251256

252257
cy.step('Verify selection persists after mouse leave');
253258
cy.get('[data-index="1"]').trigger('mouseleave', { force: true });
254-
cy.wait(100);
255259
cy.get('[data-index="1"]').should('satisfy', ($el) => {
256260
return $el.hasClass('bg-accent') || $el.attr('aria-selected') === 'true';
257261
});
@@ -262,18 +266,21 @@ describe('Command', () => {
262266
cy.get('#chat-input').should('exist');
263267

264268
cy.step('Filter for Picture command');
265-
cy.get('#chat-input').click().clear();
269+
cy.get('#chat-input').click();
270+
cy.get('#chat-input').clear();
266271
cy.get('#chat-input').type('/pic');
267272
cy.get('[data-index]').should('have.length', 1);
268273
cy.get('[data-index="0"]').should('contain', 'Picture');
269274

270275
cy.step('Filter for Canvas command');
271-
cy.get('#chat-input').clear().type('/can');
276+
cy.get('#chat-input').clear();
277+
cy.get('#chat-input').type('/can');
272278
cy.get('[data-index]').should('have.length', 1);
273279
cy.get('[data-index="0"]').should('contain', 'Canvas');
274280

275281
cy.step('Filter with non-matching text');
276-
cy.get('#chat-input').clear().type('/xyz');
282+
cy.get('#chat-input').clear();
283+
cy.get('#chat-input').type('/xyz');
277284
cy.get('[data-index]').should('not.exist');
278285
});
279286

@@ -330,7 +337,9 @@ describe('Command', () => {
330337
cy.get('#chat-input').should('exist');
331338

332339
cy.step('Select persistent Sticky command');
333-
cy.get('#chat-input').click().clear().type('/sti');
340+
cy.get('#chat-input').click();
341+
cy.get('#chat-input').clear();
342+
cy.get('#chat-input').type('/sti');
334343
cy.get('[data-index]').should('have.length.at.least', 1);
335344
cy.get('[data-index]').contains('Sticky').click();
336345

@@ -399,7 +408,7 @@ describe('Command', () => {
399408
});
400409

401410
cy.get('body').click(0, 0);
402-
cy.wait(200);
411+
cy.get('[data-popover-content]').should('not.exist');
403412

404413
cy.step('Verify inline menu contains all commands');
405414
cy.get('#chat-input').type('/');
@@ -441,7 +450,8 @@ describe('Command', () => {
441450
cy.get('[data-index]').should('not.exist');
442451

443452
cy.step('Send regular message without commands');
444-
cy.get('#chat-input').clear().type('Regular message{enter}');
453+
cy.get('#chat-input').clear();
454+
cy.get('#chat-input').type('Regular message{enter}');
445455
cy.get('.step').last().should('contain', 'Command:');
446456
});
447457
});

0 commit comments

Comments
 (0)