Skip to content

Commit 2af98c1

Browse files
authored
Fix close button styling in date range modal (#495)
* Fix close button styling in date range modal * Stabilize citation selectors in e2e tests. Add stable data-testid hooks and retryable citation waits to reduce flakes in real-api runs.
1 parent 8861a5f commit 2af98c1

File tree

4 files changed

+69
-34
lines changed

4 files changed

+69
-34
lines changed

e2e/helpers/llmbot-post.ts

Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,7 @@ export class LLMBotPostHelper {
8686
* @param postId - Optional post ID to scope the search
8787
*/
8888
getCitationIcon(index: number, postId?: string): Locator {
89-
const baseLocator = postId ? this.getLLMBotPost(postId) : this.getLLMBotPost();
90-
// Look for citation wrapper spans, which contain the SVG icon
91-
return baseLocator.locator('[class*="CitationWrapper"] svg').nth(index - 1);
89+
return this.getCitationWrapper(index, postId).locator('svg');
9290
}
9391

9492
/**
@@ -97,8 +95,7 @@ export class LLMBotPostHelper {
9795
*/
9896
getAllCitationIcons(postId?: string): Locator {
9997
const baseLocator = postId ? this.getLLMBotPost(postId) : this.getLLMBotPost();
100-
// Look for citation wrapper spans, which contain the SVG icon
101-
return baseLocator.locator('[class*="CitationWrapper"] svg');
98+
return baseLocator.locator('[data-testid="llm-citation"]');
10299
}
103100

104101
/**
@@ -107,7 +104,7 @@ export class LLMBotPostHelper {
107104
*/
108105
getCitationTooltip(postId?: string): Locator {
109106
// Tooltip is rendered at page level, not inside post container
110-
return this.page.locator('[class*="TooltipContainer"]').first();
107+
return this.page.locator('[data-testid="llm-citation-tooltip"]').first();
111108
}
112109

113110
/**
@@ -117,7 +114,7 @@ export class LLMBotPostHelper {
117114
*/
118115
getCitationWrapper(index: number, postId?: string): Locator {
119116
const baseLocator = postId ? this.getLLMBotPost(postId) : this.getLLMBotPost();
120-
return baseLocator.locator('[class*="CitationWrapper"]').nth(index - 1);
117+
return baseLocator.locator(`[data-testid="llm-citation"][data-citation-index="${index}"]`);
121118
}
122119

123120
/**
@@ -393,22 +390,54 @@ export class LLMBotPostHelper {
393390
* @param maxTimeout - Maximum wait time in ms (default: 5 minutes)
394391
*/
395392
async waitForCitation(index: number, postId?: string, maxTimeout: number = 300000): Promise<void> {
396-
const citation = this.getCitationIcon(index, postId);
393+
const citation = this.getCitationWrapper(index, postId);
394+
const allCitations = this.getAllCitationIcons(postId);
397395

398396
// Poll every 500ms checking if citation has appeared
399397
const startTime = Date.now();
400398
while (Date.now() - startTime < maxTimeout) {
401-
const isVisible = await citation.isVisible().catch(() => false);
402-
if (isVisible) {
403-
// Citation found - wait a bit for final updates
404-
await this.page.waitForTimeout(500);
405-
return;
399+
const count = await allCitations.count().catch(() => 0);
400+
if (count >= index) {
401+
const isVisible = await citation.isVisible().catch(() => false);
402+
if (isVisible) {
403+
// Citation found - wait a bit for final updates
404+
await this.page.waitForTimeout(500);
405+
return;
406+
}
406407
}
407408
await this.page.waitForTimeout(500);
408409
}
409410

410411
// If we hit max timeout, throw error
411-
throw new Error(`Timeout waiting for citation ${index} to appear`);
412+
const count = await allCitations.count().catch(() => 0);
413+
throw new Error(`Timeout waiting for citation ${index} to appear (found ${count})`);
414+
}
415+
416+
/**
417+
* Wait for citation to appear with retry via regenerate
418+
* @param index - Citation index (1-based)
419+
* @param postId - Optional post ID to scope the wait
420+
* @param maxTimeout - Maximum wait time per attempt in ms (default: 2 minutes)
421+
* @param retries - Number of regenerate retries (default: 1)
422+
*/
423+
async waitForCitationWithRetry(
424+
index: number,
425+
postId?: string,
426+
maxTimeout: number = 120000,
427+
retries: number = 1,
428+
): Promise<void> {
429+
for (let attempt = 0; attempt <= retries; attempt++) {
430+
try {
431+
await this.waitForCitation(index, postId, maxTimeout);
432+
return;
433+
} catch (error) {
434+
if (attempt >= retries) {
435+
throw error;
436+
}
437+
await this.regenerateResponse(postId);
438+
await this.waitForStreamingComplete();
439+
}
440+
}
412441
}
413442

414443
/**

e2e/tests/llmbot-post-component/citations-annotations.spec.ts

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ const password = 'regularuser';
3636

3737
const config = getAPIConfig();
3838
const skipMessage = getSkipMessage();
39+
const citationInstruction = 'Use the web_search tool and include at least one citation in your response. Do not answer without citations.';
40+
41+
function withCitationInstruction(prompt: string): string {
42+
return `${citationInstruction} ${prompt}`;
43+
}
3944

4045
async function setupTestPage(page, mattermost, provider: ProviderBundle) {
4146
const mmPage = new MattermostPage(page);
@@ -89,13 +94,13 @@ function createProviderTestSuite(provider: ProviderBundle) {
8994
? 'Search the web for TypeScript documentation and briefly summarize 2-3 key features'
9095
: 'Use web search to find TypeScript best practices and briefly list 2-3 points with citations';
9196

92-
await aiPlugin.sendMessage(prompt);
97+
await aiPlugin.sendMessage(withCitationInstruction(prompt));
9398

9499
// Wait for streaming to complete (smart wait, up to 5 min)
95100
await llmBotHelper.waitForStreamingComplete();
96101

97102
// Wait for at least one citation to appear (smart wait, up to 5 min)
98-
await llmBotHelper.waitForCitation(1);
103+
await llmBotHelper.waitForCitationWithRetry(1, undefined, 60000);
99104

100105
const citations = llmBotHelper.getAllCitationIcons();
101106
const count = await citations.count();
@@ -120,13 +125,13 @@ function createProviderTestSuite(provider: ProviderBundle) {
120125

121126
const prompt = 'Search the web for TypeScript documentation and briefly summarize with citations (2-3 sentences)';
122127

123-
await aiPlugin.sendMessage(prompt);
128+
await aiPlugin.sendMessage(withCitationInstruction(prompt));
124129

125130
// Wait for streaming to complete (smart wait, up to 5 min)
126131
await llmBotHelper.waitForStreamingComplete();
127132

128133
// Wait for citation to appear (smart wait, up to 5 min)
129-
await llmBotHelper.waitForCitation(1);
134+
await llmBotHelper.waitForCitationWithRetry(1, undefined, 60000);
130135

131136
const citations = llmBotHelper.getAllCitationIcons();
132137
const count = await citations.count();
@@ -160,13 +165,13 @@ function createProviderTestSuite(provider: ProviderBundle) {
160165

161166
const prompt = 'Search the web for TypeScript official website and cite it';
162167

163-
await aiPlugin.sendMessage(prompt);
168+
await aiPlugin.sendMessage(withCitationInstruction(prompt));
164169

165170
// Wait for streaming to complete (smart wait, up to 5 min)
166171
await llmBotHelper.waitForStreamingComplete();
167172

168173
// Wait for citation to appear (smart wait, up to 5 min)
169-
await llmBotHelper.waitForCitation(1);
174+
await llmBotHelper.waitForCitationWithRetry(1, undefined, 60000);
170175

171176
const citations = llmBotHelper.getAllCitationIcons();
172177
const count = await citations.count();
@@ -205,14 +210,14 @@ function createProviderTestSuite(provider: ProviderBundle) {
205210
? 'Search the web for TypeScript, JavaScript, and React and briefly compare them with citations (1 paragraph)'
206211
: 'Use web search to find TypeScript, JavaScript, React info and briefly compare with citations (1 paragraph)';
207212

208-
await aiPlugin.sendMessage(prompt);
213+
await aiPlugin.sendMessage(withCitationInstruction(prompt));
209214

210215
// Wait for streaming to complete (smart wait, up to 5 min)
211216
await llmBotHelper.waitForStreamingComplete();
212217

213218
// Wait for multiple citations to appear (smart wait, up to 5 min)
214-
await llmBotHelper.waitForCitation(1);
215-
await llmBotHelper.waitForCitation(2);
219+
await llmBotHelper.waitForCitationWithRetry(1, undefined, 60000);
220+
await llmBotHelper.waitForCitationWithRetry(2, undefined, 60000);
216221

217222
const citations = llmBotHelper.getAllCitationIcons();
218223
const count = await citations.count();
@@ -261,13 +266,13 @@ function createProviderTestSuite(provider: ProviderBundle) {
261266

262267
const prompt = 'Search the web for TypeScript documentation and briefly describe it with citations (1 paragraph)';
263268

264-
await aiPlugin.sendMessage(prompt);
269+
await aiPlugin.sendMessage(withCitationInstruction(prompt));
265270

266271
// Wait for streaming to complete (smart wait, up to 5 min)
267272
await llmBotHelper.waitForStreamingComplete();
268273

269274
// Wait for citation to appear (smart wait, up to 5 min)
270-
await llmBotHelper.waitForCitation(1);
275+
await llmBotHelper.waitForCitationWithRetry(1, undefined, 60000);
271276

272277
const citationsBefore = llmBotHelper.getAllCitationIcons();
273278
const countBefore = await citationsBefore.count();
@@ -307,13 +312,13 @@ function createProviderTestSuite(provider: ProviderBundle) {
307312

308313
const prompt = 'Search the web for 1-2 TypeScript code examples with markdown formatting and citations (brief)';
309314

310-
await aiPlugin.sendMessage(prompt);
315+
await aiPlugin.sendMessage(withCitationInstruction(prompt));
311316

312317
// Wait for streaming to complete (smart wait, up to 5 min)
313318
await llmBotHelper.waitForStreamingComplete();
314319

315320
// Wait for citation to appear (smart wait, up to 5 min)
316-
await llmBotHelper.waitForCitation(1);
321+
await llmBotHelper.waitForCitationWithRetry(1, undefined, 60000);
317322

318323
const postText = llmBotHelper.getPostText();
319324
await expect(postText).toBeVisible();
@@ -340,13 +345,13 @@ function createProviderTestSuite(provider: ProviderBundle) {
340345

341346
const prompt = 'Search the web for TypeScript official documentation and cite it';
342347

343-
await aiPlugin.sendMessage(prompt);
348+
await aiPlugin.sendMessage(withCitationInstruction(prompt));
344349

345350
// Wait for streaming to complete (smart wait, up to 5 min)
346351
await llmBotHelper.waitForStreamingComplete();
347352

348353
// Wait for citation to appear (smart wait, up to 5 min)
349-
await llmBotHelper.waitForCitation(1);
354+
await llmBotHelper.waitForCitationWithRetry(1, undefined, 60000);
350355

351356
const citations = llmBotHelper.getAllCitationIcons();
352357
const count = await citations.count();

webapp/src/components/citations/citation_component.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,12 @@ export const CitationComponent = (props: CitationComponentProps) => {
3737
onMouseEnter={() => setShowTooltip(true)}
3838
onMouseLeave={() => setShowTooltip(false)}
3939
onClick={handleClick}
40+
data-testid='llm-citation'
41+
data-citation-index={props.annotation.index}
4042
>
4143
<CitationIcon size={12}/>
4244
{showTooltip && (
43-
<TooltipContainer>
45+
<TooltipContainer data-testid='llm-citation-tooltip'>
4446
<TooltipContent>
4547
<FaviconIcon domain={domain}/>
4648
<TooltipDomain>{domain}</TooltipDomain>

webapp/src/components/summarize_date_range_modal.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ const ModalContainer = styled.div`
3434
const ModalHeader = styled.div`
3535
display: flex;
3636
justify-content: space-between;
37-
align-items: flex-start;
37+
align-items: center;
3838
padding: 24px 32px;
3939
`;
4040

@@ -70,10 +70,9 @@ const CloseButton = styled.button`
7070
background: none;
7171
border: none;
7272
cursor: pointer;
73-
padding: 0;
73+
padding: 10px;
7474
border-radius: 4px;
75-
color: var(--center-channel-color);
76-
opacity: 0.64;
75+
color: rgba(var(--center-channel-color-rgb), 0.64);
7776
display: flex;
7877
align-items: center;
7978
justify-content: center;

0 commit comments

Comments
 (0)