Skip to content

Commit b59c0ce

Browse files
jackfranklinDevtools-frontend LUCI CQ
authored andcommitted
AI: only show "Ask AI" button for supported Insights
Bug: 400350312 Change-Id: I7d3b1d41876e342328e4b6357d596d48636d8899 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/6349183 Reviewed-by: Nancy Li <[email protected]> Commit-Queue: Nancy Li <[email protected]> Auto-Submit: Jack Franklin <[email protected]>
1 parent 12187c4 commit b59c0ce

File tree

5 files changed

+56
-15
lines changed

5 files changed

+56
-15
lines changed

front_end/panels/timeline/components/insights/BaseInsightComponent.test.ts

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ const {html} = Lit;
1818

1919
describeWithEnvironment('BaseInsightComponent', () => {
2020
const {BaseInsightComponent} = Insights.BaseInsightComponent;
21-
class TestInsightComponent extends BaseInsightComponent<Trace.Insights.Types.InsightModel> {
21+
class TestInsightComponentNoAISupport extends BaseInsightComponent<Trace.Insights.Types.InsightModel> {
2222
override internalName = 'test-insight';
2323
override createOverlays(): TimelineOverlay[] {
2424
return [];
@@ -27,11 +27,22 @@ describeWithEnvironment('BaseInsightComponent', () => {
2727
return html`<div>test content</div>`;
2828
}
2929
}
30-
customElements.define('test-insight-component', TestInsightComponent);
30+
class TestInsightComponentWithAISupport extends BaseInsightComponent<Trace.Insights.Types.InsightModel> {
31+
override internalName = 'test-insight';
32+
protected override hasAskAISupport = true;
33+
override createOverlays(): TimelineOverlay[] {
34+
return [];
35+
}
36+
override renderContent(): Lit.LitTemplate {
37+
return html`<div>test content</div>`;
38+
}
39+
}
40+
customElements.define('test-insight-component-no-ai-support', TestInsightComponentNoAISupport);
41+
customElements.define('test-insight-component-ai-support', TestInsightComponentWithAISupport);
3142

3243
describe('sidebar insight component rendering', () => {
3344
it('renders insight title even when not active', async () => {
34-
const component = new TestInsightComponent();
45+
const component = new TestInsightComponentNoAISupport();
3546
component.selected = false;
3647
component.model = {
3748
insightKey: 'LCPPhases',
@@ -57,7 +68,7 @@ describeWithEnvironment('BaseInsightComponent', () => {
5768
});
5869

5970
it('renders title, description and content when toggled', async () => {
60-
const component = new TestInsightComponent();
71+
const component = new TestInsightComponentNoAISupport();
6172
component.selected = true;
6273
component.model = {
6374
insightKey: 'LCPPhases',
@@ -99,8 +110,10 @@ describeWithEnvironment('BaseInsightComponent', () => {
99110
state: 'fail',
100111
frameId: '123',
101112
} as const;
102-
async function renderComponent(): Promise<TestInsightComponent> {
103-
const component = new TestInsightComponent();
113+
async function renderComponent({insightHasAISupport}: {insightHasAISupport: boolean}):
114+
Promise<TestInsightComponentNoAISupport|TestInsightComponentWithAISupport> {
115+
const component =
116+
insightHasAISupport ? new TestInsightComponentWithAISupport() : new TestInsightComponentNoAISupport();
104117
component.selected = true;
105118
component.model = FAKE_LCP_MODEL;
106119
// We don't need a real trace for these tests.
@@ -111,27 +124,40 @@ describeWithEnvironment('BaseInsightComponent', () => {
111124
return component;
112125
}
113126

114-
it('renders the "Ask AI" button when perf insights AI is enabled', async () => {
127+
it('renders the "Ask AI" button when perf insights AI is enabled and the Insight supports it', async () => {
115128
updateHostConfig({
116129
devToolsAiAssistancePerformanceAgent: {
117130
enabled: true,
118131
insightsEnabled: true,
119132
}
120133
});
121-
const component = await renderComponent();
134+
const component = await renderComponent({insightHasAISupport: true});
122135
assert.isOk(component.shadowRoot);
123136
const button = component.shadowRoot.querySelector('devtools-button[data-insights-ask-ai]');
124137
assert.isOk(button);
125138
});
126139

140+
it('does not show the button if the feature is enabled but the Insight does not support it', async () => {
141+
updateHostConfig({
142+
devToolsAiAssistancePerformanceAgent: {
143+
enabled: true,
144+
insightsEnabled: true,
145+
}
146+
});
147+
const component = await renderComponent({insightHasAISupport: false});
148+
assert.isOk(component.shadowRoot);
149+
const button = component.shadowRoot.querySelector('devtools-button[data-insights-ask-ai]');
150+
assert.isNull(button);
151+
});
152+
127153
it('sets the context when the user clicks the button', async () => {
128154
updateHostConfig({
129155
devToolsAiAssistancePerformanceAgent: {
130156
enabled: true,
131157
insightsEnabled: true,
132158
}
133159
});
134-
const component = await renderComponent();
160+
const component = await renderComponent({insightHasAISupport: true});
135161
assert.isOk(component.shadowRoot);
136162
const button = component.shadowRoot.querySelector('devtools-button[data-insights-ask-ai]');
137163
assert.isOk(button);
@@ -152,7 +178,7 @@ describeWithEnvironment('BaseInsightComponent', () => {
152178
it('clears the active context when it gets toggled shut', async () => {
153179
const FAKE_ACTIVE_INSIGHT = {} as unknown as Utils.InsightAIContext.ActiveInsight;
154180
UI.Context.Context.instance().setFlavor(Utils.InsightAIContext.ActiveInsight, FAKE_ACTIVE_INSIGHT);
155-
const component = await renderComponent();
181+
const component = await renderComponent({insightHasAISupport: true});
156182
const header = component.shadowRoot?.querySelector('header');
157183
assert.isOk(header);
158184
dispatchClickEvent(header);
@@ -166,7 +192,8 @@ describeWithEnvironment('BaseInsightComponent', () => {
166192
enabled: false,
167193
}
168194
});
169-
const component = await renderComponent();
195+
const component = await renderComponent(
196+
{insightHasAISupport: true}); // The Insight supports it, but the feature is not enabled
170197
assert.isOk(component.shadowRoot);
171198
const button = component.shadowRoot.querySelector('devtools-button[data-insights-ask-ai]');
172199
assert.isNull(button);
@@ -179,7 +206,7 @@ describeWithEnvironment('BaseInsightComponent', () => {
179206
insightsEnabled: false,
180207
}
181208
});
182-
const component = await renderComponent();
209+
const component = await renderComponent({insightHasAISupport: true});
183210
assert.isOk(component.shadowRoot);
184211
const button = component.shadowRoot.querySelector('devtools-button[data-insights-ask-ai]');
185212
assert.isNull(button);

front_end/panels/timeline/components/insights/BaseInsightComponent.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,13 +66,20 @@ export abstract class BaseInsightComponent<T extends InsightModel> extends HTMLE
6666

6767
protected readonly shadow = this.attachShadow({mode: 'open'});
6868

69+
// Flipped to true for Insights that have support for the "Ask AI" Insights
70+
// experience. The "Ask AI" button will only be shown for an Insight if this
71+
// is true and if the feature has been enabled by the user and they meet the
72+
// requirements to use AI.
73+
protected readonly hasAskAISupport: boolean = false;
74+
// This flag tracks if the Insights AI feature is enabled within Chrome for
75+
// the active user.
76+
#insightsAskAiEnabled = false;
77+
6978
#selected = false;
7079
#model: T|null = null;
7180
#parsedTrace: Trace.Handlers.Types.ParsedTrace|null = null;
7281
#fieldMetrics: Trace.Insights.Common.CrUXFieldMetricResults|null = null;
7382

74-
#insightsAskAiEnabled = false;
75-
7683
get model(): T|null {
7784
return this.#model;
7885
}
@@ -315,6 +322,10 @@ export abstract class BaseInsightComponent<T extends InsightModel> extends HTMLE
315322
void action.execute();
316323
}
317324

325+
#canShowAskAI(): boolean {
326+
return this.#insightsAskAiEnabled && this.hasAskAISupport;
327+
}
328+
318329
#renderInsightContent(insightModel: T): Lit.LitTemplate {
319330
if (!this.#selected) {
320331
return Lit.nothing;
@@ -327,7 +338,7 @@ export abstract class BaseInsightComponent<T extends InsightModel> extends HTMLE
327338
<div class="insight-body">
328339
<div class="insight-description">${md(insightModel.description)}</div>
329340
<div class="insight-content">${content}</div>
330-
${this.#insightsAskAiEnabled ? html`
341+
${this.#canShowAskAI() ? html`
331342
<div class="ask-ai-btn-wrap">
332343
<devtools-button class="ask-ai"
333344
.variant=${Buttons.Button.Variant.OUTLINED}

front_end/panels/timeline/components/insights/LCPDiscovery.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ function getImageData(model: LCPDiscoveryInsightModel): LCPImageDiscoveryData|nu
6262
export class LCPDiscovery extends BaseInsightComponent<LCPDiscoveryInsightModel> {
6363
static override readonly litTagName = Lit.StaticHtml.literal`devtools-performance-lcp-discovery`;
6464
override internalName = 'lcp-discovery';
65+
protected override hasAskAISupport = true;
6566

6667
#renderDiscoveryDelay(delay: Trace.Types.Timing.Micro): Element {
6768
const timeWrapper = document.createElement('span');

front_end/panels/timeline/components/insights/LCPPhases.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ interface PhaseData {
2525
export class LCPPhases extends BaseInsightComponent<LCPPhasesInsightModel> {
2626
static override readonly litTagName = Lit.StaticHtml.literal`devtools-performance-lcp-by-phases`;
2727
override internalName = 'lcp-by-phase';
28+
protected override hasAskAISupport = true;
2829
#overlay: Overlays.Overlays.TimespanBreakdown|null = null;
2930

3031
#getPhaseData(): PhaseData[] {

front_end/panels/timeline/components/insights/RenderBlocking.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const {html} = Lit;
2020

2121
export class RenderBlocking extends BaseInsightComponent<RenderBlockingInsightModel> {
2222
static override readonly litTagName = Lit.StaticHtml.literal`devtools-performance-render-blocking-requests`;
23+
protected override hasAskAISupport = true;
2324
override internalName = 'render-blocking-requests';
2425

2526
override createOverlays(): Overlays.Overlays.TimelineOverlay[] {

0 commit comments

Comments
 (0)