Skip to content

Commit 812bc9b

Browse files
Samiya CaurDevtools-frontend LUCI CQ
authored andcommitted
Fix violations in AISettingsTab & migrate it to extend UI.Widget.VBox
Fixed: 407751702 Change-Id: I4b95136448e165ab32ed86609d27daf05efd1963 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/7085212 Reviewed-by: Ergün Erdoğmuş <[email protected]> Commit-Queue: Samiya Caur <[email protected]>
1 parent a8a24f5 commit 812bc9b

File tree

4 files changed

+414
-454
lines changed

4 files changed

+414
-454
lines changed

front_end/panels/settings/AISettingsTab.test.ts

Lines changed: 100 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,19 @@
55
import * as Common from '../../core/common/common.js';
66
import * as Host from '../../core/host/host.js';
77
import type * as Platform from '../../core/platform/platform.js';
8+
import type {LocalizedString} from '../../core/platform/UIString.js';
89
import * as AiAssistanceModel from '../../models/ai_assistance/ai_assistance.js';
910
import {renderElementIntoDOM} from '../../testing/DOMHelpers.js';
1011
import {describeWithEnvironment, updateHostConfig} from '../../testing/EnvironmentHelpers.js';
12+
import {createViewFunctionStub, type ViewFunctionStub} from '../../testing/ViewFunctionHelpers.js';
1113
import * as Switch from '../../ui/components/switch/switch.js';
1214

1315
import * as Settings from './settings.js';
1416

15-
async function drainMicroTasks() {
16-
await new Promise(resolve => setTimeout(resolve, 0));
17-
}
18-
1917
describeWithEnvironment('AISettingsTab', () => {
2018
let deleteAiAssistanceHistoryStub:
2119
sinon.SinonStub<Parameters<typeof AiAssistanceModel.AiHistoryStorage.AiHistoryStorage.prototype.deleteAll>>;
22-
let view: Settings.AISettingsTab.AISettingsTab|undefined;
20+
let aidaAccessStub: sinon.SinonStub<[], Promise<Host.AidaClient.AidaAccessPreconditions>>;
2321

2422
beforeEach(async () => {
2523
deleteAiAssistanceHistoryStub =
@@ -33,11 +31,12 @@ describeWithEnvironment('AISettingsTab', () => {
3331
enabled: true,
3432
}
3533
});
34+
aidaAccessStub = sinon.stub(Host.AidaClient.AidaClient, 'checkAccessPreconditions');
35+
aidaAccessStub.returns(Promise.resolve(Host.AidaClient.AidaAccessPreconditions.AVAILABLE));
3636
});
3737

3838
afterEach(async () => {
39-
await drainMicroTasks();
40-
view?.remove();
39+
aidaAccessStub.restore();
4140
});
4241

4342
function mockHostConfigWithExplainThisResourceEnabled() {
@@ -49,72 +48,48 @@ describeWithEnvironment('AISettingsTab', () => {
4948
});
5049
}
5150

52-
function isExpanded(details: Element): boolean {
53-
return details.classList.contains('open');
54-
}
55-
56-
async function renderAISettings(): Promise<{
57-
switches: Switch.Switch.Switch[],
58-
details: Element[],
59-
dropdownButtons: HTMLElement[],
60-
toggleContainers: HTMLElement[],
61-
view: Settings.AISettingsTab.AISettingsTab,
51+
async function setupWidget(): Promise<{
52+
widget: Settings.AISettingsTab.AISettingsTab,
53+
view: ViewFunctionStub<typeof Settings.AISettingsTab.AISettingsTab>,
6254
}> {
63-
Common.Settings.moduleSetting('console-insights-enabled').set(false);
64-
Common.Settings.moduleSetting('ai-assistance-enabled').set(true);
65-
Common.Settings.moduleSetting('ai-annotations-enabled').set(true);
66-
Common.Settings.moduleSetting('ai-code-completion-enabled').set(false);
67-
68-
view = new Settings.AISettingsTab.AISettingsTab();
69-
renderElementIntoDOM(view);
70-
await view.render();
71-
assert.isNotNull(view.shadowRoot);
72-
73-
const switches = Array.from(view.shadowRoot.querySelectorAll('devtools-switch'));
74-
assert.lengthOf(switches, 4);
75-
const details = Array.from(view.shadowRoot.querySelectorAll('.whole-row'));
76-
assert.lengthOf(details, 4);
77-
const dropdownButtons = Array.from(view.shadowRoot.querySelectorAll('.dropdown devtools-button')) as HTMLElement[];
78-
assert.lengthOf(dropdownButtons, 4);
79-
const toggleContainers =
80-
Array.from(view.shadowRoot.querySelectorAll('.toggle-container')) as Switch.Switch.Switch[];
81-
assert.lengthOf(toggleContainers, 4);
82-
return {switches, details, dropdownButtons, toggleContainers, view};
55+
const view = createViewFunctionStub(Settings.AISettingsTab.AISettingsTab);
56+
const widget = new Settings.AISettingsTab.AISettingsTab(view);
57+
const container = document.createElement('div');
58+
renderElementIntoDOM(container);
59+
widget.markAsRoot();
60+
widget.show(container);
61+
await view.nextInput;
62+
return {widget, view};
8363
}
8464

85-
it('renders', async () => {
65+
it('renders disclaimers and settings', async () => {
8666
Common.Settings.moduleSetting('console-insights-enabled').set(true);
8767
Common.Settings.moduleSetting('ai-assistance-enabled').set(true);
8868
Common.Settings.moduleSetting('ai-annotations-enabled').set(true);
8969
Common.Settings.moduleSetting('ai-code-completion-enabled').set(true);
9070

91-
view = new Settings.AISettingsTab.AISettingsTab();
92-
renderElementIntoDOM(view);
93-
await view.render();
94-
assert.isNotNull(view.shadowRoot);
71+
const {view} = await setupWidget();
9572

96-
const sharedDisclaimerHeader = view.shadowRoot.querySelector('.shared-disclaimer h2');
97-
assert.strictEqual(sharedDisclaimerHeader?.textContent, 'Boost your productivity with AI');
98-
99-
const disclaimers = view.shadowRoot.querySelectorAll('.shared-disclaimer .disclaimer-list div');
73+
const disclaimers = view.input.sharedDisclaimerBulletPoints;
74+
const sendDataDiscalimer = disclaimers[1].text as LocalizedString;
10075
assert.strictEqual(
101-
disclaimers[3].textContent,
76+
sendDataDiscalimer,
10277
'These features send relevant data to Google. Google collects this data and feedback to improve its products and services with the help of human reviewers. Avoid sharing sensitive or personal information.');
103-
assert.strictEqual(disclaimers[5].textContent, 'Depending on your region, Google may refrain from data collection');
78+
const dataCollectionDiscalimer = disclaimers[2].text as LocalizedString;
79+
assert.strictEqual(dataCollectionDiscalimer, 'Depending on your region, Google may refrain from data collection');
10480

105-
const settingCards = view.shadowRoot.querySelectorAll('.setting-card h2');
106-
const settingNames = Array.from(settingCards).map(element => element.textContent);
81+
const settingToParams = view.input.settingToParams;
82+
const settingParams = Array.from(settingToParams.values());
83+
const settingNames = settingParams.map(setting => setting.settingName);
10784
assert.deepEqual(settingNames, ['Console Insights', 'AI assistance', 'Auto annotations', 'Code suggestions']);
108-
109-
const settingCardDesc = view.shadowRoot.querySelectorAll('.setting-description');
110-
assert.strictEqual(settingCardDesc[0].textContent, 'Helps you understand and fix console warnings and errors');
111-
assert.strictEqual(settingCardDesc[1].textContent, 'Get help with understanding CSS styles');
85+
assert.strictEqual(settingParams[0].settingDescription, 'Helps you understand and fix console warnings and errors');
86+
assert.strictEqual(settingParams[1].settingDescription, 'Get help with understanding CSS styles');
11287
assert.strictEqual(
113-
settingCardDesc[2].textContent, 'Automatically generate titles for performance trace annotations');
114-
assert.strictEqual(settingCardDesc[3].textContent, 'Get help completing your code');
88+
settingParams[2].settingDescription, 'Automatically generate titles for performance trace annotations');
89+
assert.strictEqual(settingParams[3].settingDescription, 'Get help completing your code');
11590
});
11691

117-
it('renders different dislaimers for managed users which have logging disabled', async () => {
92+
it('has different dislaimers for managed users which have logging disabled', async () => {
11893
Common.Settings.moduleSetting('console-insights-enabled').set(true);
11994
Common.Settings.moduleSetting('ai-assistance-enabled').set(true);
12095
Common.Settings.moduleSetting('ai-annotations-enabled').set(true);
@@ -130,73 +105,88 @@ describeWithEnvironment('AISettingsTab', () => {
130105
},
131106
});
132107

133-
view = new Settings.AISettingsTab.AISettingsTab();
134-
renderElementIntoDOM(view);
135-
await view.render();
136-
assert.isNotNull(view.shadowRoot);
108+
const {view} = await setupWidget();
137109

138-
const disclaimers = view.shadowRoot.querySelectorAll('.shared-disclaimer .disclaimer-list div');
110+
const disclaimers = view.input.sharedDisclaimerBulletPoints;
111+
const sendDataNoLogging = disclaimers[1].text as LocalizedString;
139112
assert.strictEqual(
140-
disclaimers[3].textContent,
113+
sendDataNoLogging,
141114
'Your content will not be used by human reviewers to improve AI. Your organization may change these settings at any time.');
115+
const dataCollectionNoLogging = disclaimers[2].text as LocalizedString;
142116
assert.strictEqual(
143-
disclaimers[5].textContent,
117+
dataCollectionNoLogging,
144118
'Depending on your Google account management and/or region, Google may refrain from data collection');
145119
});
146120

147-
it('renders with explain this resource enabled', async () => {
148-
mockHostConfigWithExplainThisResourceEnabled();
121+
it('has explain this resource enabled', async () => {
149122
Common.Settings.moduleSetting('console-insights-enabled').set(true);
150123
Common.Settings.moduleSetting('ai-assistance-enabled').set(true);
124+
mockHostConfigWithExplainThisResourceEnabled();
151125

152-
view = new Settings.AISettingsTab.AISettingsTab();
153-
renderElementIntoDOM(view);
154-
await view.render();
155-
assert.isNotNull(view.shadowRoot);
126+
const {view} = await setupWidget();
156127

157-
const settingCardDesc = view.shadowRoot.querySelectorAll('.setting-description');
158-
assert.strictEqual(settingCardDesc[1].textContent, 'Get help with understanding CSS styles, and network requests');
128+
const settingToParams = view.input.settingToParams.entries();
129+
settingToParams.next();
130+
const explainThisResource = settingToParams.next();
131+
assert.exists(explainThisResource.value);
132+
assert.isFalse(explainThisResource.value[0].disabled());
133+
assert.strictEqual(
134+
explainThisResource.value[1].settingDescription,
135+
'Get help with understanding CSS styles, and network requests');
159136
});
160137

161138
it('can turn feature on, which automatically expands it', async () => {
162-
const {switches, details} = await renderAISettings();
139+
Common.Settings.moduleSetting('console-insights-enabled').set(false);
140+
const {view} = await setupWidget();
141+
163142
assert.isFalse(Common.Settings.moduleSetting('console-insights-enabled').get());
164-
assert.isFalse(isExpanded(details[0]));
143+
const setting = view.input.settingToParams.entries().next();
144+
assert.exists(setting.value);
145+
assert.isFalse(setting.value[1].settingExpandState.isSettingExpanded);
165146

166-
switches[0].dispatchEvent(new Switch.Switch.SwitchChangeEvent(true));
147+
view.input.toggleSetting(setting.value[0], new Switch.Switch.SwitchChangeEvent(true));
167148
assert.isTrue(Common.Settings.moduleSetting('console-insights-enabled').get());
168-
assert.isTrue(isExpanded(details[0]));
149+
assert.isTrue(setting.value[1].settingExpandState.isSettingExpanded);
169150
});
170151

171-
it('can expand and collaps details via click', async () => {
172-
const {details, dropdownButtons} = await renderAISettings();
173-
assert.isFalse(isExpanded(details[0]));
152+
it('can expand and collapse details via click', async () => {
153+
Common.Settings.moduleSetting('console-insights-enabled').set(false);
154+
const {view} = await setupWidget();
155+
156+
const setting = view.input.settingToParams.entries().next();
157+
assert.exists(setting.value);
158+
assert.isFalse(setting.value[1].settingExpandState.isSettingExpanded);
174159
assert.isFalse(Common.Settings.moduleSetting('console-insights-enabled').get());
175160

176-
dropdownButtons[0].click();
177-
assert.isTrue(isExpanded(details[0]));
161+
view.input.expandSetting(setting.value[0]);
178162
assert.isFalse(Common.Settings.moduleSetting('console-insights-enabled').get());
163+
assert.isTrue(setting.value[1].settingExpandState.isSettingExpanded);
179164

180-
dropdownButtons[0].click();
181-
assert.isFalse(isExpanded(details[0]));
165+
view.input.expandSetting(setting.value[0]);
182166
assert.isFalse(Common.Settings.moduleSetting('console-insights-enabled').get());
167+
assert.isFalse(setting.value[1].settingExpandState.isSettingExpanded);
183168
});
184169

185170
it('can turn feature off without collapsing it', async () => {
186-
const {switches, details, dropdownButtons} = await renderAISettings();
187-
dropdownButtons[1].click();
171+
Common.Settings.moduleSetting('ai-assistance-enabled').set(true);
172+
const {view} = await setupWidget();
173+
174+
const settingToParams = view.input.settingToParams.entries();
175+
settingToParams.next();
176+
const setting = settingToParams.next();
177+
assert.exists(setting.value);
178+
179+
view.input.expandSetting(setting.value[0]);
188180
assert.isTrue(Common.Settings.moduleSetting('ai-assistance-enabled').get());
189-
assert.isTrue(isExpanded(details[1]));
181+
assert.isTrue(setting.value[1].settingExpandState.isSettingExpanded);
190182

191-
(switches[1].parentElement as HTMLElement).click();
183+
view.input.toggleSetting(setting.value[0], new MouseEvent('click'));
192184
assert.isFalse(Common.Settings.moduleSetting('ai-assistance-enabled').get());
193-
assert.isTrue(isExpanded(details[1]));
185+
assert.isTrue(setting.value[1].settingExpandState.isSettingExpanded);
194186
});
195187

196188
it('disables switches if blocked by age', async () => {
197189
const underAgeExplainer = 'This feature is only available to users who are 18 years of age or older.';
198-
const aidaAccessStub = sinon.stub(Host.AidaClient.AidaClient, 'checkAccessPreconditions');
199-
aidaAccessStub.returns(Promise.resolve(Host.AidaClient.AidaAccessPreconditions.AVAILABLE));
200190
updateHostConfig({
201191
aidaAvailability: {
202192
blockedByAge: true,
@@ -209,50 +199,28 @@ describeWithEnvironment('AISettingsTab', () => {
209199
},
210200
});
211201

212-
const {switches, toggleContainers, view} = await renderAISettings();
213-
assert.strictEqual(view.shadowRoot?.querySelector('.disabled-explainer')?.textContent?.trim(), underAgeExplainer);
214-
assert.isTrue(switches[0].disabled);
215-
assert.strictEqual(toggleContainers[0].title, underAgeExplainer);
216-
assert.isTrue(switches[1].disabled);
217-
assert.strictEqual(toggleContainers[1].title, underAgeExplainer);
218-
assert.isTrue(switches[2].disabled);
219-
assert.strictEqual(toggleContainers[2].title, underAgeExplainer);
220-
assert.isTrue(switches[3].disabled);
221-
assert.strictEqual(toggleContainers[3].title, underAgeExplainer);
202+
const {view} = await setupWidget();
222203

223-
aidaAccessStub.restore();
204+
assert.deepEqual(view.input.disabledReasons, [underAgeExplainer]);
224205
});
225206

226207
it('updates when the user logs in', async () => {
227208
const notLoggedInExplainer = 'This feature is only available when you sign into Chrome with your Google account.';
228-
const aidaAccessStub = sinon.stub(Host.AidaClient.AidaClient, 'checkAccessPreconditions');
229209
aidaAccessStub.returns(Promise.resolve(Host.AidaClient.AidaAccessPreconditions.NO_ACCOUNT_EMAIL));
230210

231-
const {switches, toggleContainers, view} = await renderAISettings();
232-
assert.strictEqual(
233-
view.shadowRoot?.querySelector('.disabled-explainer')?.textContent?.trim(), notLoggedInExplainer);
234-
assert.isTrue(switches[0].disabled);
235-
assert.strictEqual(toggleContainers[0].title, notLoggedInExplainer);
236-
assert.isTrue(switches[1].disabled);
237-
assert.strictEqual(toggleContainers[1].title, notLoggedInExplainer);
238-
assert.isTrue(switches[2].disabled);
239-
assert.strictEqual(toggleContainers[2].title, notLoggedInExplainer);
240-
assert.isTrue(switches[3].disabled);
241-
assert.strictEqual(toggleContainers[3].title, notLoggedInExplainer);
211+
const {view} = await setupWidget();
212+
213+
assert.deepEqual(view.input.disabledReasons, [notLoggedInExplainer]);
242214

243215
aidaAccessStub.returns(Promise.resolve(Host.AidaClient.AidaAccessPreconditions.AVAILABLE));
244216
Host.AidaClient.HostConfigTracker.instance().dispatchEventToListeners(
245217
Host.AidaClient.Events.AIDA_AVAILABILITY_CHANGED);
246-
await drainMicroTasks();
247-
assert.isNull(view.shadowRoot?.querySelector('.disabled-explainer'));
248-
assert.isFalse(switches[0].disabled);
249-
assert.isFalse(switches[1].disabled);
250-
assert.isFalse(switches[2].disabled);
251-
assert.isFalse(switches[3].disabled);
252-
aidaAccessStub.restore();
218+
await view.nextInput;
219+
220+
assert.deepEqual(view.input.disabledReasons, []);
253221
});
254222

255-
it('renders disabled switch component with reason', async () => {
223+
it('updates disabled reason', async () => {
256224
Common.Settings.moduleSetting('console-insights-enabled').setRegistration({
257225
settingName: 'console-insights-enabled',
258226
settingType: Common.Settings.SettingType.BOOLEAN,
@@ -269,24 +237,23 @@ describeWithEnvironment('AISettingsTab', () => {
269237
return {disabled: true, reasons: ['some reason' as Platform.UIString.LocalizedString]};
270238
},
271239
});
272-
const stub = sinon.stub(Host.AidaClient.AidaClient, 'checkAccessPreconditions');
273-
stub.returns(Promise.resolve(Host.AidaClient.AidaAccessPreconditions.AVAILABLE));
274-
275-
const {switches, toggleContainers, view} = await renderAISettings();
276-
assert.strictEqual(view.shadowRoot?.querySelector('.disabled-explainer')?.textContent?.trim(), 'some reason');
277-
assert.isTrue(switches[0].disabled);
278-
assert.strictEqual(toggleContainers[0].title, 'some reason');
279-
assert.isTrue(switches[1].disabled);
280-
assert.strictEqual(toggleContainers[1].title, 'some reason');
281-
stub.restore();
240+
241+
const {view} = await setupWidget();
242+
243+
assert.deepEqual(view.input.disabledReasons, ['some reason']);
282244
});
283245

284246
it('can turn feature off and clear history', async () => {
285-
const {switches} = await renderAISettings();
286247
Common.Settings.moduleSetting('ai-assistance-enabled').set(true);
287248
Common.Settings.moduleSetting('ai-assistance-history-entries').set([{}, {}]);
249+
const {view} = await setupWidget();
250+
251+
const settingToParams = view.input.settingToParams.entries();
252+
settingToParams.next();
253+
const setting = settingToParams.next();
254+
assert.exists(setting.value);
288255

289-
(switches[1].parentElement as HTMLElement).click();
256+
view.input.toggleSetting(setting.value[0], new MouseEvent('click'));
290257
assert.isFalse(Common.Settings.moduleSetting('ai-assistance-enabled').get());
291258
assert.isTrue(
292259
deleteAiAssistanceHistoryStub.called, 'Expected AiHistoryStorage deleteAll to be called but it is not called');

0 commit comments

Comments
 (0)