Render clarify search prompts as guidance only
This commit is contained in:
@@ -94,11 +94,11 @@ export const DEFAULT_COMMON_QUESTIONS: readonly SearchQuestionChip[] = withQuest
|
||||
export const DEFAULT_CLARIFYING_QUESTIONS: readonly SearchQuestionChip[] = withQuestionKind([
|
||||
{
|
||||
key: 'ui.search.question.default.clarify_target',
|
||||
fallback: 'Which workload, release, or policy should I narrow this to?',
|
||||
fallback: 'Add the workload, release, or policy you want to inspect.',
|
||||
},
|
||||
{
|
||||
key: 'ui.search.question.default.clarify_scope',
|
||||
fallback: 'What exact blocker or symptom should I narrow this to?',
|
||||
fallback: 'Add the exact blocker or symptom you want to inspect.',
|
||||
},
|
||||
], 'clarify');
|
||||
|
||||
@@ -153,8 +153,8 @@ const FINDINGS_SELF_SERVE: SearchSelfServeDefinition = {
|
||||
{ key: 'ui.search.question.findings.remediation', fallback: 'What is the safest remediation path?' },
|
||||
], 'page'),
|
||||
clarifyingQuestions: withQuestionKind([
|
||||
{ key: 'ui.search.question.findings.clarify_target', fallback: 'Which CVE, workload, or package should I narrow this to?' },
|
||||
{ key: 'ui.search.question.findings.clarify_scope', fallback: 'What part of this finding matters most right now?' },
|
||||
{ key: 'ui.search.question.findings.clarify_target', fallback: 'Add the CVE, workload, or package you want to inspect.' },
|
||||
{ key: 'ui.search.question.findings.clarify_scope', fallback: 'Add the part of the finding that matters most right now.' },
|
||||
], 'clarify'),
|
||||
};
|
||||
|
||||
@@ -165,8 +165,8 @@ const VEX_SELF_SERVE: SearchSelfServeDefinition = {
|
||||
{ key: 'ui.search.question.vex.conflicting_evidence', fallback: 'What evidence conflicts with this VEX?' },
|
||||
], 'page'),
|
||||
clarifyingQuestions: withQuestionKind([
|
||||
{ key: 'ui.search.question.vex.clarify_statement', fallback: 'Which statement, component, or product range should I narrow this to?' },
|
||||
{ key: 'ui.search.question.vex.clarify_need', fallback: 'Do you want exploitability meaning, coverage, or conflict evidence?' },
|
||||
{ key: 'ui.search.question.vex.clarify_statement', fallback: 'Add the statement, component, or product range you want to inspect.' },
|
||||
{ key: 'ui.search.question.vex.clarify_need', fallback: 'Add whether you need exploitability meaning, coverage, or conflict evidence.' },
|
||||
], 'clarify'),
|
||||
};
|
||||
|
||||
@@ -177,8 +177,8 @@ const POLICY_SELF_SERVE: SearchSelfServeDefinition = {
|
||||
{ key: 'ui.search.question.policy.exception', fallback: 'What is the safest exception path?' },
|
||||
], 'page'),
|
||||
clarifyingQuestions: withQuestionKind([
|
||||
{ key: 'ui.search.question.policy.clarify_target', fallback: 'Which rule, environment, or control should I narrow this to?' },
|
||||
{ key: 'ui.search.question.policy.clarify_need', fallback: 'Do you want recent failures, exceptions, or promotion impact?' },
|
||||
{ key: 'ui.search.question.policy.clarify_target', fallback: 'Add the rule, environment, or control you want to inspect.' },
|
||||
{ key: 'ui.search.question.policy.clarify_need', fallback: 'Add whether you need recent failures, exceptions, or promotion impact.' },
|
||||
], 'clarify'),
|
||||
};
|
||||
|
||||
@@ -189,8 +189,8 @@ const DOCTOR_SELF_SERVE: SearchSelfServeDefinition = {
|
||||
{ key: 'ui.search.question.doctor.changed', fallback: 'What changed before this health issue?' },
|
||||
], 'page'),
|
||||
clarifyingQuestions: withQuestionKind([
|
||||
{ key: 'ui.search.question.doctor.clarify_check', fallback: 'Which check or symptom should I narrow this to?' },
|
||||
{ key: 'ui.search.question.doctor.clarify_need', fallback: 'Do you want diagnosis, remediation, or verification steps?' },
|
||||
{ key: 'ui.search.question.doctor.clarify_check', fallback: 'Add the check or symptom you want to inspect.' },
|
||||
{ key: 'ui.search.question.doctor.clarify_need', fallback: 'Add whether you need diagnosis, remediation, or verification steps.' },
|
||||
], 'clarify'),
|
||||
};
|
||||
|
||||
@@ -201,8 +201,8 @@ const GRAPH_SELF_SERVE: SearchSelfServeDefinition = {
|
||||
{ key: 'ui.search.question.graph.next_hop', fallback: 'What should I inspect next on this path?' },
|
||||
], 'page'),
|
||||
clarifyingQuestions: withQuestionKind([
|
||||
{ key: 'ui.search.question.graph.clarify_node', fallback: 'Which node, package, or edge should I narrow this to?' },
|
||||
{ key: 'ui.search.question.graph.clarify_need', fallback: 'Do you want reachability, impact, or next-step guidance?' },
|
||||
{ key: 'ui.search.question.graph.clarify_node', fallback: 'Add the node, package, or edge you want to inspect.' },
|
||||
{ key: 'ui.search.question.graph.clarify_need', fallback: 'Add whether you need reachability, impact, or next-step guidance.' },
|
||||
], 'clarify'),
|
||||
};
|
||||
|
||||
@@ -213,8 +213,8 @@ const OPS_MEMORY_SELF_SERVE: SearchSelfServeDefinition = {
|
||||
{ key: 'ui.search.question.ops_memory.repeat', fallback: 'What repeated failures are related to this?' },
|
||||
], 'page'),
|
||||
clarifyingQuestions: withQuestionKind([
|
||||
{ key: 'ui.search.question.ops_memory.clarify_job', fallback: 'Which job, incident, or recurring failure should I narrow this to?' },
|
||||
{ key: 'ui.search.question.ops_memory.clarify_need', fallback: 'Do you want precedent, likely cause, or recommended recovery?' },
|
||||
{ key: 'ui.search.question.ops_memory.clarify_job', fallback: 'Add the job, incident, or recurring failure you want to inspect.' },
|
||||
{ key: 'ui.search.question.ops_memory.clarify_need', fallback: 'Add whether you need precedent, likely cause, or recommended recovery.' },
|
||||
], 'clarify'),
|
||||
};
|
||||
|
||||
@@ -225,8 +225,8 @@ const TIMELINE_SELF_SERVE: SearchSelfServeDefinition = {
|
||||
{ key: 'ui.search.question.timeline.next_event', fallback: 'What else happened around this event?' },
|
||||
], 'page'),
|
||||
clarifyingQuestions: withQuestionKind([
|
||||
{ key: 'ui.search.question.timeline.clarify_window', fallback: 'Which deployment, incident, or time window should I narrow this to?' },
|
||||
{ key: 'ui.search.question.timeline.clarify_need', fallback: 'Do you want causes, impacts, or follow-up events?' },
|
||||
{ key: 'ui.search.question.timeline.clarify_window', fallback: 'Add the deployment, incident, or time window you want to inspect.' },
|
||||
{ key: 'ui.search.question.timeline.clarify_need', fallback: 'Add whether you need causes, impacts, or follow-up events.' },
|
||||
], 'clarify'),
|
||||
};
|
||||
|
||||
@@ -237,8 +237,8 @@ const RELEASES_SELF_SERVE: SearchSelfServeDefinition = {
|
||||
{ key: 'ui.search.question.releases.next_step', fallback: 'What is the safest next step to ship?' },
|
||||
], 'page'),
|
||||
clarifyingQuestions: withQuestionKind([
|
||||
{ key: 'ui.search.question.releases.clarify_target', fallback: 'Which environment or release should I narrow this to?' },
|
||||
{ key: 'ui.search.question.releases.clarify_need', fallback: 'Do you want blockers, approvals, or policy impact?' },
|
||||
{ key: 'ui.search.question.releases.clarify_target', fallback: 'Add the environment or release you want to inspect.' },
|
||||
{ key: 'ui.search.question.releases.clarify_need', fallback: 'Add whether you need blockers, approvals, or policy impact.' },
|
||||
], 'clarify'),
|
||||
};
|
||||
|
||||
|
||||
@@ -78,6 +78,8 @@ type SearchAnswerView = {
|
||||
}>;
|
||||
questionLabel: string;
|
||||
questions: SearchQuestionView[];
|
||||
guidanceLabel: string | null;
|
||||
guidance: SearchQuestionView[];
|
||||
nextSearches: SearchSuggestionView[];
|
||||
};
|
||||
type SuccessfulSearchHistoryEntry = {
|
||||
@@ -202,6 +204,22 @@ type SuccessfulSearchHistoryEntry = {
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (answer.guidance.length > 0) {
|
||||
<div class="search__answer-guidance">
|
||||
<div class="search__group-label">{{ answer.guidanceLabel }}</div>
|
||||
<div class="search__guidance-list">
|
||||
@for (question of answer.guidance; track question.query) {
|
||||
<div
|
||||
class="search__guidance-item"
|
||||
data-answer-guidance="clarify"
|
||||
>
|
||||
{{ question.query }}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (answer.nextSearches.length > 0) {
|
||||
<div class="search__answer-next">
|
||||
<div class="search__group-label">{{ t('ui.search.answer.next_searches', 'Related searches') }}</div>
|
||||
@@ -681,6 +699,26 @@ type SuccessfulSearchHistoryEntry = {
|
||||
border-style: dashed;
|
||||
}
|
||||
|
||||
.search__answer-guidance {
|
||||
margin-top: 0.75rem;
|
||||
}
|
||||
|
||||
.search__guidance-list {
|
||||
display: grid;
|
||||
gap: 0.5rem;
|
||||
padding: 0.25rem 0.75rem 0;
|
||||
}
|
||||
|
||||
.search__guidance-item {
|
||||
padding: 0.5rem 0.625rem;
|
||||
border: 1px dashed var(--color-border-secondary);
|
||||
border-radius: var(--radius-sm);
|
||||
background: var(--color-surface-primary);
|
||||
color: var(--color-text-secondary);
|
||||
font-size: 0.75rem;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.search__answer {
|
||||
margin: 0.625rem 0.75rem 0.5rem;
|
||||
padding: 0.75rem;
|
||||
@@ -1174,7 +1212,15 @@ export class GlobalSearchComponent implements OnInit, OnDestroy {
|
||||
questionLabel: backendAnswer.status === 'clarify'
|
||||
? this.t('ui.search.answer.questions.clarify', 'Try one of these')
|
||||
: this.t('ui.search.answer.questions.follow_up', 'Related questions'),
|
||||
questions: this.buildBackendAnswerQuestions(backendAnswer),
|
||||
questions: backendAnswer.status === 'clarify'
|
||||
? []
|
||||
: this.buildBackendAnswerQuestions(backendAnswer),
|
||||
guidanceLabel: backendAnswer.status === 'clarify'
|
||||
? this.t('ui.search.answer.guidance.clarify', 'Narrow the search by adding')
|
||||
: null,
|
||||
guidance: backendAnswer.status === 'clarify'
|
||||
? this.buildClarifyGuidance(backendAnswer)
|
||||
: [],
|
||||
nextSearches,
|
||||
};
|
||||
}
|
||||
@@ -1191,6 +1237,8 @@ export class GlobalSearchComponent implements OnInit, OnDestroy {
|
||||
citations: this.buildAnswerCitations(response),
|
||||
questionLabel: this.t('ui.search.answer.questions.follow_up', 'Related questions'),
|
||||
questions: this.commonQuestions().slice(0, 3),
|
||||
guidanceLabel: null,
|
||||
guidance: [],
|
||||
nextSearches,
|
||||
};
|
||||
}
|
||||
@@ -1211,8 +1259,10 @@ export class GlobalSearchComponent implements OnInit, OnDestroy {
|
||||
'No direct grounded answer was found from the current page context.',
|
||||
),
|
||||
citations: [],
|
||||
questionLabel: this.t('ui.search.answer.questions.clarify', 'Try one of these'),
|
||||
questions: clarifyingQuestions,
|
||||
questionLabel: '',
|
||||
questions: [],
|
||||
guidanceLabel: this.t('ui.search.answer.guidance.clarify', 'Narrow the search by adding'),
|
||||
guidance: clarifyingQuestions,
|
||||
nextSearches,
|
||||
};
|
||||
}
|
||||
@@ -1233,6 +1283,8 @@ export class GlobalSearchComponent implements OnInit, OnDestroy {
|
||||
citations: [],
|
||||
questionLabel: this.t('ui.search.answer.questions.retry', 'Try one of these'),
|
||||
questions: this.commonQuestions().slice(0, 2),
|
||||
guidanceLabel: null,
|
||||
guidance: [],
|
||||
nextSearches,
|
||||
};
|
||||
});
|
||||
@@ -1974,6 +2026,21 @@ export class GlobalSearchComponent implements OnInit, OnDestroy {
|
||||
: this.commonQuestions().slice(0, 3);
|
||||
}
|
||||
|
||||
private buildClarifyGuidance(answer: NonNullable<UnifiedSearchResponse['contextAnswer']>): SearchQuestionView[] {
|
||||
const backendGuidance = (answer.questions ?? [])
|
||||
.filter((question) => question.query.trim().length > 0)
|
||||
.map((question) => ({
|
||||
query: question.query,
|
||||
kind: 'clarify' as const,
|
||||
}));
|
||||
|
||||
if (backendGuidance.length > 0) {
|
||||
return backendGuidance.slice(0, 3);
|
||||
}
|
||||
|
||||
return this.clarifyingQuestions().slice(0, 3);
|
||||
}
|
||||
|
||||
private answerTitleForStatus(status: SearchAnswerView['status']): string {
|
||||
switch (status) {
|
||||
case 'clarify':
|
||||
|
||||
@@ -199,8 +199,8 @@ describe('AmbientContextService', () => {
|
||||
events.next(new NavigationEnd(1, '/ops/policy', '/ops/policy'));
|
||||
|
||||
const questions = service.getClarifyingQuestions().map((item) => item.fallback);
|
||||
expect(questions).toContain('Which rule, environment, or control should I narrow this to?');
|
||||
expect(questions).toContain('Do you want recent failures, exceptions, or promotion impact?');
|
||||
expect(questions).toContain('Add the rule, environment, or control you want to inspect.');
|
||||
expect(questions).toContain('Add whether you need recent failures, exceptions, or promotion impact.');
|
||||
});
|
||||
|
||||
it('returns timeline self-serve questions after navigating to the timeline route', () => {
|
||||
@@ -215,8 +215,8 @@ describe('AmbientContextService', () => {
|
||||
|
||||
expect(commonQuestions).toContain('What changed before this incident?');
|
||||
expect(commonQuestions).toContain('Which release introduced this risk?');
|
||||
expect(clarifyingQuestions).toContain('Which deployment, incident, or time window should I narrow this to?');
|
||||
expect(clarifyingQuestions).toContain('Do you want causes, impacts, or follow-up events?');
|
||||
expect(clarifyingQuestions).toContain('Add the deployment, incident, or time window you want to inspect.');
|
||||
expect(clarifyingQuestions).toContain('Add whether you need causes, impacts, or follow-up events.');
|
||||
});
|
||||
|
||||
it('returns release-control self-serve questions for release routes without forcing a search domain', () => {
|
||||
@@ -233,7 +233,7 @@ describe('AmbientContextService', () => {
|
||||
expect(panel?.titleFallback).toBe('Release control');
|
||||
expect(commonQuestions).toContain('What blocked this promotion?');
|
||||
expect(commonQuestions).toContain('Which approvals are missing?');
|
||||
expect(clarifyingQuestions).toContain('Which environment or release should I narrow this to?');
|
||||
expect(clarifyingQuestions).toContain('Do you want blockers, approvals, or policy impact?');
|
||||
expect(clarifyingQuestions).toContain('Add the environment or release you want to inspect.');
|
||||
expect(clarifyingQuestions).toContain('Add whether you need blockers, approvals, or policy impact.');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -158,12 +158,12 @@ describe('GlobalSearchComponent', () => {
|
||||
ambientContext.getClarifyingQuestions.and.returnValue([
|
||||
{
|
||||
key: 'ui.search.question.findings.clarify_target',
|
||||
fallback: 'Which CVE, workload, or package should I narrow this to?',
|
||||
fallback: 'Add the CVE, workload, or package you want to inspect.',
|
||||
kind: 'clarify',
|
||||
},
|
||||
{
|
||||
key: 'ui.search.question.findings.clarify_scope',
|
||||
fallback: 'What part of this finding matters most right now?',
|
||||
fallback: 'Add the part of the finding that matters most right now.',
|
||||
kind: 'clarify',
|
||||
},
|
||||
]);
|
||||
@@ -807,11 +807,15 @@ describe('GlobalSearchComponent', () => {
|
||||
const answerQuestions = Array.from(
|
||||
fixture.nativeElement.querySelectorAll('[data-answer-question]') as NodeListOf<HTMLButtonElement>,
|
||||
).map((node) => node.textContent?.trim());
|
||||
const guidanceItems = Array.from(
|
||||
fixture.nativeElement.querySelectorAll('[data-answer-guidance="clarify"]') as NodeListOf<HTMLElement>,
|
||||
).map((node) => node.textContent?.trim());
|
||||
|
||||
expect(answerPanel).not.toBeNull();
|
||||
expect(answerPanel?.textContent).toContain('Tighten the question');
|
||||
expect(answerQuestions).toContain('Which CVE, workload, or package should I narrow this to?');
|
||||
expect(answerQuestions).toContain('What part of this finding matters most right now?');
|
||||
expect(answerQuestions).toEqual([]);
|
||||
expect(guidanceItems).toContain('Add the CVE, workload, or package you want to inspect.');
|
||||
expect(guidanceItems).toContain('Add the part of the finding that matters most right now.');
|
||||
});
|
||||
|
||||
it('does not hard-filter search requests to the current route scope', async () => {
|
||||
|
||||
@@ -342,6 +342,26 @@ test.describe('Unified Search - Experience Quality UX', () => {
|
||||
await expect(page.locator('.search__group').filter({ hasText: 'Recent' })).toHaveCount(0);
|
||||
});
|
||||
|
||||
test('renders release clarify guidance as non-executable hints on /releases/versions', async ({ page }) => {
|
||||
await mockSearchResponses(page, () => emptyResponse('mystery release blocker'));
|
||||
|
||||
await page.goto('/releases/versions');
|
||||
await expect(page.locator('aside.sidebar')).toBeVisible({ timeout: 15_000 });
|
||||
|
||||
await typeInSearch(page, 'mystery release blocker');
|
||||
await waitForResults(page);
|
||||
|
||||
await expect(page.locator('[data-answer-status="clarify"]')).toBeVisible();
|
||||
await expect(page.locator('[data-answer-guidance="clarify"]').filter({
|
||||
hasText: /add the environment or release you want to inspect\./i,
|
||||
})).toHaveCount(1);
|
||||
await expect(page.locator('[data-answer-guidance="clarify"]').filter({
|
||||
hasText: /add whether you need blockers, approvals, or policy impact\./i,
|
||||
})).toHaveCount(1);
|
||||
await expect(page.getByRole('button', { name: 'Add the environment or release you want to inspect.' })).toHaveCount(0);
|
||||
await expect(page.locator('app-global-search input[type="text"]')).toHaveValue('mystery release blocker');
|
||||
});
|
||||
|
||||
test('uses backend answer framing and shows overflow as secondary results without manual filters', async ({ page }) => {
|
||||
await mockSearchResponses(page, (query) =>
|
||||
query.includes('critical findings')
|
||||
|
||||
@@ -230,48 +230,6 @@ const doctorGroundedResponse = buildResponse(
|
||||
},
|
||||
);
|
||||
|
||||
const timelineGroundedResponse = buildResponse(
|
||||
'Which deployment, incident, or time window should I narrow this to?',
|
||||
[
|
||||
timelineCard({
|
||||
eventId: 'incident-db-spike',
|
||||
title: 'Database latency spike before promotion',
|
||||
snippet: 'A deployment preceded the incident and introduced the latency spike that now blocks the rollout.',
|
||||
}),
|
||||
],
|
||||
undefined,
|
||||
{
|
||||
contextAnswer: {
|
||||
status: 'grounded',
|
||||
code: 'retrieved_scope_weighted_evidence',
|
||||
summary: 'The timeline shows a deployment immediately before the incident that introduced the blocking latency spike.',
|
||||
reason: 'Timeline evidence became grounded once the time window was narrowed.',
|
||||
evidence: 'Grounded in 1 source across Timeline.',
|
||||
citations: [
|
||||
{
|
||||
entityKey: 'timeline:incident-db-spike',
|
||||
title: 'Database latency spike before promotion',
|
||||
domain: 'timeline',
|
||||
},
|
||||
],
|
||||
},
|
||||
coverage: {
|
||||
currentScopeDomain: 'timeline',
|
||||
currentScopeWeighted: true,
|
||||
domains: [
|
||||
{
|
||||
domain: 'timeline',
|
||||
candidateCount: 1,
|
||||
visibleCardCount: 1,
|
||||
topScore: 0.9,
|
||||
isCurrentScope: true,
|
||||
hasVisibleResults: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const releasesGroundedResponse = buildResponse(
|
||||
'What blocked this promotion?',
|
||||
[
|
||||
@@ -437,7 +395,7 @@ test.describe('Unified Search - Priority Route Self-Serve Journeys', () => {
|
||||
}).first()).toBeVisible();
|
||||
});
|
||||
|
||||
test('recovers a timeline search from clarify to grounded with the route-owned narrowing question', async ({ page }) => {
|
||||
test('shows timeline narrowing guidance instead of a runnable clarify prompt', async ({ page }) => {
|
||||
await mockPriorityRouteSearch(page);
|
||||
|
||||
await openRoute(page, '/ops/timeline');
|
||||
@@ -445,14 +403,12 @@ test.describe('Unified Search - Priority Route Self-Serve Journeys', () => {
|
||||
await waitForResults(page);
|
||||
|
||||
await expect(page.locator('[data-answer-status="clarify"]')).toBeVisible();
|
||||
await page.getByRole('button', { name: 'Which deployment, incident, or time window should I narrow this to?' }).click();
|
||||
await waitForResults(page);
|
||||
await waitForEntityCards(page, 1);
|
||||
|
||||
await expect(page.locator('app-global-search input[type="text"]')).toHaveValue(
|
||||
'Which deployment, incident, or time window should I narrow this to?',
|
||||
);
|
||||
await expect(page.locator('[data-answer-status="grounded"]')).toContainText(/deployment immediately before the incident/i);
|
||||
await expect(page.locator('[data-answer-guidance="clarify"]')).toContainText([
|
||||
'Add the deployment, incident, or time window you want to inspect.',
|
||||
'Add whether you need causes, impacts, or follow-up events.',
|
||||
]);
|
||||
await expect(page.getByRole('button', { name: 'Add the deployment, incident, or time window you want to inspect.' })).toHaveCount(0);
|
||||
await expect(page.locator('app-global-search input[type="text"]')).toHaveValue('spike');
|
||||
});
|
||||
|
||||
test('reruns a policy next-search inside the current policy route context', async ({ page }) => {
|
||||
@@ -557,10 +513,6 @@ function resolvePriorityRouteResponse(currentRoute: string, query: string): unkn
|
||||
}
|
||||
|
||||
if (currentRoute.includes('/ops/timeline')) {
|
||||
if (query.includes('which deployment, incident, or time window should i narrow this to')) {
|
||||
return timelineGroundedResponse;
|
||||
}
|
||||
|
||||
if (query.includes('spike')) {
|
||||
return emptyResponse('spike');
|
||||
}
|
||||
|
||||
@@ -46,25 +46,6 @@ const groundedFindingResponse = {
|
||||
},
|
||||
};
|
||||
|
||||
const narrowedFindingResponse = buildResponse(
|
||||
'Which CVE, workload, or package should I narrow this to?',
|
||||
[
|
||||
findingCard({
|
||||
cveId: 'CVE-2023-38545',
|
||||
title: 'CVE-2023-38545 in edge-router',
|
||||
snippet: 'A narrowed finding result is now grounded and ready for review.',
|
||||
severity: 'high',
|
||||
}),
|
||||
],
|
||||
{
|
||||
summary: 'Narrowing the question exposed a grounded finding answer.',
|
||||
template: 'finding_overview',
|
||||
confidence: 'high',
|
||||
sourceCount: 1,
|
||||
domainsCovered: ['Findings'],
|
||||
},
|
||||
);
|
||||
|
||||
test.describe('Unified Search - Self-Serve Answer Panel', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await setupBasicMocks(page);
|
||||
@@ -103,12 +84,8 @@ test.describe('Unified Search - Self-Serve Answer Panel', () => {
|
||||
await expect(answerPanel.locator('[data-answer-citation]')).toContainText(['CVE-2024-21626 in api-gateway']);
|
||||
});
|
||||
|
||||
test('uses clarify questions to rerun search and recover a grounded answer', async ({ page }) => {
|
||||
test('shows clarify guidance without rendering dead clarify prompts as buttons', async ({ page }) => {
|
||||
await mockSearchResponses(page, (query) => {
|
||||
if (query.includes('which cve, workload, or package should i narrow this to?')) {
|
||||
return narrowedFindingResponse;
|
||||
}
|
||||
|
||||
if (query.includes('mystery issue')) {
|
||||
return emptyResponse('mystery issue');
|
||||
}
|
||||
@@ -122,17 +99,18 @@ test.describe('Unified Search - Self-Serve Answer Panel', () => {
|
||||
await typeInSearch(page, 'mystery issue');
|
||||
await waitForResults(page);
|
||||
await expect(page.locator('[data-answer-status="clarify"]')).toBeVisible();
|
||||
|
||||
await page.getByRole('button', { name: 'Which CVE, workload, or package should I narrow this to?' }).click();
|
||||
await waitForResults(page);
|
||||
await waitForEntityCards(page, 1);
|
||||
|
||||
await expect(page.locator('app-global-search input[type="text"]')).toHaveValue(
|
||||
'Which CVE, workload, or package should I narrow this to?',
|
||||
);
|
||||
await expect(page.locator('[data-answer-status="grounded"]')).toContainText(
|
||||
'Narrowing the question exposed a grounded finding answer.',
|
||||
);
|
||||
await expect(
|
||||
page.locator('[data-answer-guidance="clarify"]').filter({
|
||||
hasText: /Add the CVE, workload, or package you want to inspect\./i,
|
||||
}),
|
||||
).toHaveCount(1);
|
||||
await expect(
|
||||
page.locator('[data-answer-guidance="clarify"]').filter({
|
||||
hasText: /Add the part of the finding that matters most right now\./i,
|
||||
}),
|
||||
).toHaveCount(1);
|
||||
await expect(page.getByRole('button', { name: 'Add the CVE, workload, or package you want to inspect.' })).toHaveCount(0);
|
||||
await expect(page.locator('app-global-search input[type="text"]')).toHaveValue('mystery issue');
|
||||
});
|
||||
|
||||
test('opens AdvisoryAI from the answer panel with grounded answer context', async ({ page }) => {
|
||||
|
||||
Reference in New Issue
Block a user