UNPKG

chrome-devtools-frontend

Version:
923 lines (844 loc) • 34.9 kB
// Copyright 2023 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import * as Common from '../../../core/common/common.js'; import * as Host from '../../../core/host/host.js'; import type * as Platform from '../../../core/platform/platform.js'; import { assertScreenshot, dispatchClickEvent, getCleanTextContentFromElements, raf, renderElementIntoDOM, } from '../../../testing/DOMHelpers.js'; import {describeWithEnvironment, updateHostConfig} from '../../../testing/EnvironmentHelpers.js'; import * as Explain from '../explain.js'; describeWithEnvironment('ConsoleInsight', () => { let component: Explain.ConsoleInsight|undefined; const containerCss = ` box-sizing: border-box; background-color: aqua; `; afterEach(() => { component?.remove(); Common.Settings.settingForTest('console-insights-enabled').set(true); Common.Settings.settingForTest('console-insights-onboarding-finished').set(true); }); function getTestAidaClient() { return { async * fetch() { yield {explanation: 'test', metadata: {rpcGlobalId: 0}, completed: true}; }, registerClientEvent: sinon.spy(), }; } function getTestPromptBuilder() { return { async buildPrompt() { return { prompt: '', sources: [ { type: Explain.SourceType.MESSAGE, value: 'error message', }, ], isPageReloadRecommended: true, }; }, getSearchQuery() { return ''; }, }; } async function drainMicroTasks() { await new Promise(resolve => setTimeout(resolve, 0)); } it('shows opt-in teaser when setting is turned off', async () => { Common.Settings.settingForTest('console-insights-enabled').set(false); component = new Explain.ConsoleInsight( getTestPromptBuilder(), getTestAidaClient(), Host.AidaClient.AidaAccessPreconditions.AVAILABLE); renderElementIntoDOM(component); await drainMicroTasks(); assert.isNotNull(component.shadowRoot); assert.deepEqual( getCleanTextContentFromElements(component.shadowRoot, 'main'), [ 'Turn on Console insights in Settings to receive AI assistance for understanding and addressing console warnings and errors. Learn more', ], ); }); it('shows opt-in teaser when blocked by age', async () => { updateHostConfig({ aidaAvailability: { blockedByAge: true, }, devToolsConsoleInsights: { enabled: true, }, }); component = new Explain.ConsoleInsight( getTestPromptBuilder(), getTestAidaClient(), Host.AidaClient.AidaAccessPreconditions.AVAILABLE); renderElementIntoDOM(component); await drainMicroTasks(); assert.isNotNull(component.shadowRoot); assert.deepEqual( getCleanTextContentFromElements(component.shadowRoot, 'main'), [ 'Turn on Console insights in Settings to receive AI assistance for understanding and addressing console warnings and errors. Learn more', ], ); }); it('generates an explanation when the user logs in', async () => { component = new Explain.ConsoleInsight( getTestPromptBuilder(), getTestAidaClient(), Host.AidaClient.AidaAccessPreconditions.NO_ACCOUNT_EMAIL); renderElementIntoDOM(component); await drainMicroTasks(); assert.isNotNull(component.shadowRoot); assert.deepEqual( getCleanTextContentFromElements(component.shadowRoot, 'main'), [ 'This feature is only available when you sign into Chrome with your Google account.', ], ); const stub = sinon.stub(Host.AidaClient.AidaClient, 'checkAccessPreconditions') .returns(Promise.resolve(Host.AidaClient.AidaAccessPreconditions.AVAILABLE)); Host.AidaClient.HostConfigTracker.instance().dispatchEventToListeners( Host.AidaClient.Events.AIDA_AVAILABILITY_CHANGED); await drainMicroTasks(); assert.deepEqual(getCleanTextContentFromElements(component.shadowRoot, 'h2'), ['Explanation']); stub.restore(); }); it('shows opt-in teaser when setting is disabled via disabledCondition', async () => { const setting = Common.Settings.settingForTest('console-insights-enabled'); setting.setRegistration({ settingName: 'console-insights-enabled', settingType: Common.Settings.SettingType.BOOLEAN, defaultValue: true, disabledCondition: () => { return {disabled: true, reasons: ['disabled for test' as Platform.UIString.LocalizedString]}; }, }); component = new Explain.ConsoleInsight( getTestPromptBuilder(), getTestAidaClient(), Host.AidaClient.AidaAccessPreconditions.AVAILABLE); renderElementIntoDOM(component); await drainMicroTasks(); assert.isNotNull(component.shadowRoot); assert.deepEqual( getCleanTextContentFromElements(component.shadowRoot, 'main'), [ 'Turn on Console insights in Settings to receive AI assistance for understanding and addressing console warnings and errors. Learn more', ], ); setting.setRegistration({ settingName: 'console-insights-enabled', settingType: Common.Settings.SettingType.BOOLEAN, defaultValue: false, }); }); it('shows reminder on first run of console insights', async () => { Common.Settings.settingForTest('console-insights-onboarding-finished').set(false); component = new Explain.ConsoleInsight( getTestPromptBuilder(), getTestAidaClient(), Host.AidaClient.AidaAccessPreconditions.AVAILABLE); renderElementIntoDOM(component); await drainMicroTasks(); assert.isNotNull(component.shadowRoot); assert.strictEqual(component.shadowRoot.querySelector('h2')?.innerText, 'Understand console messages with AI'); dispatchClickEvent(component.shadowRoot.querySelector('.continue-button')!, { bubbles: true, composed: true, }); await drainMicroTasks(); // Rating buttons are shown. assert(component.shadowRoot.querySelector('.rating')); }); it('shows an error message on timeout', async () => { function getAidaClientWithTimeout() { return { async * fetch() { yield { explanation: 'test', metadata: { rpcGlobalId: 0, }, completed: true, }; throw new Error('doAidaConversation timed out'); }, registerClientEvent: sinon.spy(), }; } component = new Explain.ConsoleInsight( getTestPromptBuilder(), getAidaClientWithTimeout(), Host.AidaClient.AidaAccessPreconditions.AVAILABLE); renderElementIntoDOM(component); await drainMicroTasks(); assert.isNotNull(component.shadowRoot); assert.strictEqual( component.shadowRoot.querySelector('.error-message')?.textContent, 'Generating a response took too long. Please try again.'); }); const reportsRating = (positive: boolean, disallowLogging: boolean) => async () => { updateHostConfig({ aidaAvailability: { disallowLogging, }, }); const actionTaken = sinon.stub(Host.userMetrics, 'actionTaken'); const aidaClient = getTestAidaClient(); component = new Explain.ConsoleInsight( getTestPromptBuilder(), aidaClient, Host.AidaClient.AidaAccessPreconditions.AVAILABLE); renderElementIntoDOM(component); await drainMicroTasks(); dispatchClickEvent(component.shadowRoot!.querySelector(`.rating [data-rating=${positive}]`)!, { bubbles: true, composed: true, }); sinon.assert.calledOnce(aidaClient.registerClientEvent); sinon.assert.match(aidaClient.registerClientEvent.firstCall.firstArg, sinon.match({ corresponding_aida_rpc_global_id: 0, disable_user_content_logging: disallowLogging, do_conversation_client_event: { user_feedback: {sentiment: positive ? 'POSITIVE' : 'NEGATIVE'}, }, })); sinon.assert.calledWith( actionTaken, positive ? Host.UserMetrics.Action.InsightRatedPositive : Host.UserMetrics.Action.InsightRatedNegative); dispatchClickEvent(component.shadowRoot!.querySelector(`.rating [data-rating=${positive}]`)!, { bubbles: true, composed: true, }); // Can only rate once. sinon.assert.calledOnce(aidaClient.registerClientEvent); }; describe('without logging', () => { it('reports positive rating', reportsRating(true, true)); it('reports negative rating', reportsRating(false, true)); }); describe('with logging', () => { it('reports positive rating', reportsRating(true, false)); it('reports negative rating', reportsRating(false, false)); }); it('has thumbs up/down buttons if logging is disabled', async () => { updateHostConfig({ aidaAvailability: { disallowLogging: true, }, devToolsConsoleInsights: { enabled: true, }, }); component = new Explain.ConsoleInsight( getTestPromptBuilder(), getTestAidaClient(), Host.AidaClient.AidaAccessPreconditions.AVAILABLE); renderElementIntoDOM(component); await drainMicroTasks(); const thumbsUpButton = component.shadowRoot!.querySelector('.rating [data-rating="true"]'); assert.isNotNull(thumbsUpButton); const thumbsDownButton = component.shadowRoot!.querySelector('.rating [data-rating="false"]'); assert.isNotNull(thumbsDownButton); }); it('report if the user is not logged in', async () => { component = new Explain.ConsoleInsight( getTestPromptBuilder(), getTestAidaClient(), Host.AidaClient.AidaAccessPreconditions.NO_ACCOUNT_EMAIL); renderElementIntoDOM(component); await drainMicroTasks(); const content = component.shadowRoot!.querySelector('main')!.innerText.trim(); assert.strictEqual(content, 'This feature is only available when you sign into Chrome with your Google account.'); }); it('report if the navigator is offline', async () => { component = new Explain.ConsoleInsight( getTestPromptBuilder(), getTestAidaClient(), Host.AidaClient.AidaAccessPreconditions.NO_INTERNET); renderElementIntoDOM(component); await drainMicroTasks(); const content = component.shadowRoot!.querySelector('main')!.innerText.trim(); assert.strictEqual(content, 'Check your internet connection and try again.'); }); it('displays factuality metadata as related content', async () => { function getAidaClientWithMetadata() { return { async * fetch() { yield { explanation: 'test', metadata: { rpcGlobalId: 0, factualityMetadata: { facts: [ {sourceUri: 'https://www.firstSource.test/someInfo'}, {sourceUri: 'https://www.anotherSource.test/page'}, ], }, }, completed: true, }; }, registerClientEvent: sinon.spy(), }; } component = new Explain.ConsoleInsight( getTestPromptBuilder(), getAidaClientWithMetadata(), Host.AidaClient.AidaAccessPreconditions.AVAILABLE); renderElementIntoDOM(component); await drainMicroTasks(); const details = component.shadowRoot!.querySelector('details'); assert.strictEqual(details!.querySelector('summary')!.textContent?.trim(), 'Sources and related content'); const xLinks = details!.querySelectorAll('x-link'); assert.strictEqual(xLinks[0].textContent?.trim(), 'https://www.firstSource.test/someInfo'); assert.strictEqual(xLinks[0].getAttribute('href'), 'https://www.firstSource.test/someInfo'); assert.strictEqual(xLinks[1].textContent?.trim(), 'https://www.anotherSource.test/page'); assert.strictEqual(xLinks[1].getAttribute('href'), 'https://www.anotherSource.test/page'); }); it('displays direct citations', async () => { function getAidaClientWithMetadata() { return { async * fetch() { yield { explanation: 'This is not a real answer, it is just a test.', metadata: { rpcGlobalId: 0, attributionMetadata: { attributionAction: Host.AidaClient.RecitationAction.CITE, citations: [ { startIndex: 0, endIndex: 10, uri: 'https://www.wiki.test/directSource', sourceType: Host.AidaClient.CitationSourceType.WORLD_FACTS, }, { startIndex: 20, endIndex: 25, uri: 'https://www.world-fact.test/', sourceType: Host.AidaClient.CitationSourceType.WORLD_FACTS, }, ], }, factualityMetadata: { facts: [ {sourceUri: 'https://www.firstSource.test/someInfo'}, ], }, }, completed: true, }; }, registerClientEvent: sinon.spy(), }; } component = new Explain.ConsoleInsight( getTestPromptBuilder(), getAidaClientWithMetadata(), Host.AidaClient.AidaAccessPreconditions.AVAILABLE); renderElementIntoDOM(component); await drainMicroTasks(); const markdownView = component.shadowRoot!.querySelector('devtools-markdown-view'); assert.strictEqual( getCleanTextContentFromElements(markdownView!.shadowRoot!, '.message')[0], 'This is not[1] a real answer[2], it is just a test.'); const details = component.shadowRoot!.querySelector('details'); assert.strictEqual(details!.querySelector('summary')!.textContent?.trim(), 'Sources and related content'); const directCitations = details!.querySelectorAll('ol x-link'); assert.lengthOf(directCitations, 2); assert.strictEqual(directCitations[0].textContent?.trim(), 'https://www.wiki.test/directSource'); assert.strictEqual(directCitations[0].getAttribute('href'), 'https://www.wiki.test/directSource'); assert.strictEqual(directCitations[1].textContent?.trim(), 'https://www.world-fact.test/'); assert.strictEqual(directCitations[1].getAttribute('href'), 'https://www.world-fact.test/'); const relatedContent = details!.querySelectorAll('ul x-link'); assert.lengthOf(relatedContent, 1); assert.strictEqual(relatedContent[0].textContent?.trim(), 'https://www.firstSource.test/someInfo'); assert.strictEqual(relatedContent[0].getAttribute('href'), 'https://www.firstSource.test/someInfo'); assert.isFalse(details?.hasAttribute('open')); assert.isFalse(directCitations[0].classList.contains('highlighted')); const link = markdownView!.shadowRoot?.querySelector('sup button') as HTMLElement; link.click(); assert.isTrue(details?.hasAttribute('open')); assert.isTrue(directCitations[0].classList.contains('highlighted')); }); it('displays direct citations in code blocks', async () => { function getAidaClientWithMetadata() { return { async * fetch() { yield { explanation: `before \`\`\`\`\` const foo = document.querySelector('.some-class'); \`\`\`\`\` after `, metadata: { rpcGlobalId: 0, attributionMetadata: { attributionAction: Host.AidaClient.RecitationAction.CITE, citations: [ { startIndex: 20, endIndex: 25, uri: 'https://www.wiki.test/directSource', sourceType: Host.AidaClient.CitationSourceType.WORLD_FACTS, }, { startIndex: 30, endIndex: 38, uri: 'https://www.world-fact.test/', sourceType: Host.AidaClient.CitationSourceType.WORLD_FACTS, }, ], }, factualityMetadata: { facts: [ {sourceUri: 'https://www.firstSource.test/someInfo'}, ], }, }, completed: true, }; }, registerClientEvent: sinon.spy(), }; } component = new Explain.ConsoleInsight( getTestPromptBuilder(), getAidaClientWithMetadata(), Host.AidaClient.AidaAccessPreconditions.AVAILABLE); renderElementIntoDOM(component); await drainMicroTasks(); const markdownView = component.shadowRoot!.querySelector('devtools-markdown-view'); const codeBlock = markdownView!.shadowRoot!.querySelector('devtools-code-block'); const citations = codeBlock!.shadowRoot!.querySelectorAll('button.citation'); assert.lengthOf(citations, 2); assert.strictEqual(citations[0].textContent, '[1]'); assert.strictEqual(citations[1].textContent, '[2]'); const details = component.shadowRoot!.querySelector('details'); const directCitations = details!.querySelectorAll('ol x-link'); assert.lengthOf(directCitations, 2); assert.strictEqual(directCitations[0].textContent?.trim(), 'https://www.wiki.test/directSource'); assert.strictEqual(directCitations[0].getAttribute('href'), 'https://www.wiki.test/directSource'); assert.strictEqual(directCitations[1].textContent?.trim(), 'https://www.world-fact.test/'); assert.strictEqual(directCitations[1].getAttribute('href'), 'https://www.world-fact.test/'); assert.isFalse(details?.hasAttribute('open')); assert.isFalse(directCitations[0].classList.contains('highlighted')); (citations[0] as HTMLElement).click(); assert.isTrue(details?.hasAttribute('open')); assert.isTrue(directCitations[0].classList.contains('highlighted')); }); it('displays training data citations', async () => { function getAidaClientWithMetadata() { return { async * fetch() { yield { explanation: 'This is not a real answer, it is just a test.', metadata: { rpcGlobalId: 0, attributionMetadata: { attributionAction: Host.AidaClient.RecitationAction.CITE, citations: [ { startIndex: 5, endIndex: 9, uri: 'https://www.training.test/', sourceType: Host.AidaClient.CitationSourceType.TRAINING_DATA, }, { startIndex: 15, endIndex: 22, repository: 'chromedevtools/devtools-frontend', sourceType: Host.AidaClient.CitationSourceType.TRAINING_DATA, }, ], }, factualityMetadata: { facts: [ {sourceUri: 'https://www.firstSource.test/someInfo'}, ], }, }, completed: true, }; }, registerClientEvent: sinon.spy(), }; } component = new Explain.ConsoleInsight( getTestPromptBuilder(), getAidaClientWithMetadata(), Host.AidaClient.AidaAccessPreconditions.AVAILABLE); renderElementIntoDOM(component); await drainMicroTasks(); const details = component.shadowRoot!.querySelector('details'); assert.strictEqual(details!.querySelector('summary')!.textContent?.trim(), 'Sources and related content'); const xLinks = details!.querySelectorAll('x-link'); assert.lengthOf(xLinks, 3); assert.strictEqual(xLinks[0].textContent?.trim(), 'https://www.firstSource.test/someInfo'); assert.strictEqual(xLinks[0].getAttribute('href'), 'https://www.firstSource.test/someInfo'); assert.strictEqual(xLinks[1].textContent?.trim(), 'https://www.training.test/'); assert.strictEqual(xLinks[1].getAttribute('href'), 'https://www.training.test/'); assert.strictEqual(xLinks[2].textContent?.trim(), 'https://www.github.com/chromedevtools/devtools-frontend'); assert.strictEqual(xLinks[2].getAttribute('href'), 'https://www.github.com/chromedevtools/devtools-frontend'); }); it('deduplicates citation URLs', async () => { function getAidaClientWithMetadata() { return { async * fetch() { yield { explanation: 'This is not a real answer, it is just a test.', metadata: { rpcGlobalId: 0, attributionMetadata: { attributionAction: Host.AidaClient.RecitationAction.CITE, citations: [ { startIndex: 5, endIndex: 9, uri: 'https://www.training-and-factuality.test/', sourceType: Host.AidaClient.CitationSourceType.TRAINING_DATA, }, { startIndex: 10, endIndex: 12, uri: 'https://www.all-three.test/', sourceType: Host.AidaClient.CitationSourceType.TRAINING_DATA, }, { startIndex: 20, endIndex: 25, uri: 'https://www.world-fact-and-factuality.test/', sourceType: Host.AidaClient.CitationSourceType.WORLD_FACTS, }, { startIndex: 26, endIndex: 30, uri: 'https://www.all-three.test/', sourceType: Host.AidaClient.CitationSourceType.WORLD_FACTS, }, ], }, factualityMetadata: { facts: [ {sourceUri: 'https://www.training-and-factuality.test/'}, {sourceUri: 'https://www.world-fact-and-factuality.test/'}, {sourceUri: 'https://www.factuality.test/'}, {sourceUri: 'https://www.all-three.test/'}, ], }, }, completed: true, }; }, registerClientEvent: sinon.spy(), }; } component = new Explain.ConsoleInsight( getTestPromptBuilder(), getAidaClientWithMetadata(), Host.AidaClient.AidaAccessPreconditions.AVAILABLE); renderElementIntoDOM(component); await drainMicroTasks(); const details = component.shadowRoot!.querySelector('details'); assert.strictEqual(details!.querySelector('summary')!.textContent?.trim(), 'Sources and related content'); const xLinks = details!.querySelectorAll('x-link'); assert.lengthOf(xLinks, 4); assert.strictEqual(xLinks[0].textContent?.trim(), 'https://www.world-fact-and-factuality.test/'); assert.strictEqual(xLinks[0].getAttribute('href'), 'https://www.world-fact-and-factuality.test/'); assert.strictEqual(xLinks[1].textContent?.trim(), 'https://www.all-three.test/'); assert.strictEqual(xLinks[1].getAttribute('href'), 'https://www.all-three.test/'); assert.strictEqual(xLinks[2].textContent?.trim(), 'https://www.training-and-factuality.test/'); assert.strictEqual(xLinks[2].getAttribute('href'), 'https://www.training-and-factuality.test/'); assert.strictEqual(xLinks[3].textContent?.trim(), 'https://www.factuality.test/'); assert.strictEqual(xLinks[3].getAttribute('href'), 'https://www.factuality.test/'); }); it('renders the opt-in teaser', async () => { Common.Settings.settingForTest('console-insights-enabled').set(false); const component = new Explain.ConsoleInsight( getTestPromptBuilder(), getTestAidaClient(), Host.AidaClient.AidaAccessPreconditions.AVAILABLE); const container = document.createElement('div'); container.style.cssText = containerCss; component.style.width = '574px'; component.style.height = '64px'; container.appendChild(component); renderElementIntoDOM(container); await drainMicroTasks(); await assertScreenshot('explain/console_insight_optin.png'); }); it('renders the consent reminder', async () => { function getPromptBuilderForConsentReminder() { return { getSearchQuery() { return ''; }, async buildPrompt() { return { prompt: '', isPageReloadRecommended: false, sources: [ { type: Explain.SourceType.MESSAGE, value: 'Something went wrong\n\nSomething went wrong', }, { type: Explain.SourceType.NETWORK_REQUEST, value: `Request: https://example.com/data.html Request headers: :authority: example.com :method: GET :path: https://example.com/data.json :scheme: https accept: */* accept-encoding: gzip, deflate, br accept-language: en-DE,en;q=0.9,de-DE;q=0.8,de;q=0.7,en-US;q=0.6 referer: https://example.com/demo.html sec-ch-ua: "Not A(Brand";v="99", "Google Chrome";v="121", "Chromium";v="121" sec-ch-ua-arch: "arm" sec-ch-ua-bitness: "64" sec-ch-ua-full-version: "121.0.6116.0" sec-ch-ua-full-version-list: "Not A(Brand";v="99.0.0.0", "Google Chrome";v="121.0.6116.0", "Chromium";v="121.0.6116.0" sec-ch-ua-mobile: ?0 sec-ch-ua-model: "" sec-ch-ua-platform: "macOS" sec-ch-ua-platform-version: "14.1.0" sec-ch-ua-wow64: ?0 sec-fetch-dest: empty sec-fetch-mode: cors sec-fetch-site: same-origin user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36 Response headers: accept-ch: Sec-CH-UA, Sec-CH-UA-Arch, Sec-CH-UA-Bitness, Sec-CH-UA-Full-Version, Sec-CH-UA-Full-Version-List, Sec-CH-UA-Mobile, Sec-CH-UA-Model, Sec-CH-UA-Platform, Sec-CH-UA-Platform-Version, Sec-CH-UA-WoW64 content-length: 1646 content-type: text/html; charset=UTF-8 cross-origin-opener-policy-report-only: same-origin; report-to="gfe-static-content-corp" date: Fri, 10 Nov 2023 13:46:47 GMT permissions-policy: ch-ua=*, ch-ua-arch=*, ch-ua-bitness=*, ch-ua-full-version=*, ch-ua-full-version-list=*, ch-ua-mobile=*, ch-ua-model=*, ch-ua-platform=*, ch-ua-platform-version=*, ch-ua-wow64=* server: sffe strict-transport-security: max-age=31536000; includeSubdomains vary: Origin Response status: 404`, }, ] }; } }; } function getAidaClientForConsentReminder() { return { async * fetch() { await new Promise(resolve => setTimeout(resolve, 2000)); yield { explanation: `Some text with \`code\`. Some code: \`\`\`ts console.log('test'); document.querySelector('test').style = 'black'; \`\`\` Some text with \`code\`. Some code: \`\`\`ts console.log('test'); document.querySelector('test').style = 'black'; \`\`\` Some text with \`code\`. Some code: \`\`\`ts console.log('test'); document.querySelector('test').style = 'black'; \`\`\` `, metadata: {}, completed: true, }; }, registerClientEvent: () => Promise.resolve({}), }; } Common.Settings.Settings.instance().createLocalSetting('console-insights-onboarding-finished', false).set(false); const component = new Explain.ConsoleInsight( getPromptBuilderForConsentReminder(), getAidaClientForConsentReminder(), Host.AidaClient.AidaAccessPreconditions.AVAILABLE); const container = document.createElement('div'); container.style.cssText = containerCss; component.style.width = '574px'; component.style.height = '271px'; container.appendChild(component); renderElementIntoDOM(container); await drainMicroTasks(); await assertScreenshot('explain/console_insight_reminder.png'); }); it('renders the insight', async () => { function getPromptBuilderForInsight() { return { getSearchQuery() { return ''; }, async buildPrompt() { return { prompt: '', isPageReloadRecommended: false, sources: [ { type: Explain.SourceType.MESSAGE, value: 'Something went wrong\n\nSomething went wrong', }, { type: Explain.SourceType.STACKTRACE, value: 'Stacktrace line1\nStacketrace line2', }, { type: Explain.SourceType.RELATED_CODE, value: 'RelatedCode', }, { type: Explain.SourceType.NETWORK_REQUEST, value: `Request: https://example.com/data.html Request headers: :authority: example.com user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36 Response headers: Response status: 404`, }, ], }; }, }; } function getAidaClientForInsight() { return { async * fetch() { yield { explanation: `## Result Some text with \`code\`. Some code: \`\`\`ts console.log('test'); document.querySelector('test').style = 'black'; \`\`\` \`\`\` <!DOCTYPE html> <div>Hello world</div> <script> console.log('Hello World'); </script> \`\`\` Links: [https://example.com](https://example.com) Images: ![https://example.com](https://example.com) `, metadata: {}, completed: true, }; }, registerClientEvent: () => Promise.resolve({}), }; } const component = new Explain.ConsoleInsight( getPromptBuilderForInsight(), getAidaClientForInsight(), Host.AidaClient.AidaAccessPreconditions.AVAILABLE); component.disableAnimations = true; const container = document.createElement('div'); container.style.cssText = containerCss; component.style.width = '574px'; component.style.height = '530px'; container.appendChild(component); renderElementIntoDOM(container); // Animation are hidden and started one by one so // so we need multiple drains await drainMicroTasks(); await drainMicroTasks(); await assertScreenshot('explain/console_insight.png'); }); it('renders insights with references', async () => { function getPromptBuilderForInsight() { return { getSearchQuery() { return ''; }, async buildPrompt() { return { prompt: '', isPageReloadRecommended: false, sources: [ { type: Explain.SourceType.MESSAGE, value: 'Something went wrong\n\nSomething went wrong', }, { type: Explain.SourceType.STACKTRACE, value: 'Stacktrace line1\nStacketrace line2', }, { type: Explain.SourceType.RELATED_CODE, value: 'RelatedCode', }, { type: Explain.SourceType.NETWORK_REQUEST, value: `Request: https://example.com/data.html Request headers: :authority: example.com user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36 Response headers: Response status: 404`, }, ], }; }, }; } function getAidaClientForInsight() { return { async * fetch() { yield { explanation: `## Result Here is a text which contains both direct and indirect citations. An indirect citation is a link to a reference which applies to the whole response. A direct citation is a link to a reference, but it only applies to a specific part of the response. Direct citations are numbered and are shown as a number within square brackets in the response text. `, metadata: { attributionMetadata: { attributionAction: Host.AidaClient.RecitationAction.CITE, citations: [ { startIndex: 20, endIndex: 50, uri: 'https://www.direct-citation.dev', sourceType: Host.AidaClient.CitationSourceType.WORLD_FACTS, }, { startIndex: 170, endIndex: 176, uri: 'https://www.another-direct-citation.dev', sourceType: Host.AidaClient.CitationSourceType.WORLD_FACTS, }, ], }, factualityMetadata: { facts: [ { sourceUri: 'https://www.indirect-citation.dev', }, { sourceUri: 'https://www.the-whole-world.dev', }, { sourceUri: 'https://www.even-more-content.dev', }, ] } }, completed: true, }; }, registerClientEvent: () => Promise.resolve({}), }; } const component = new Explain.ConsoleInsight( getPromptBuilderForInsight(), getAidaClientForInsight(), Host.AidaClient.AidaAccessPreconditions.AVAILABLE); const container = document.createElement('div'); container.style.cssText = containerCss; component.style.width = '576px'; component.style.height = '463px'; container.appendChild(component); renderElementIntoDOM(container); await raf(); const detailsElement = component.shadowRoot!.querySelector('details.references')!; const transitioned = new Promise<void>(resolve => { detailsElement.addEventListener('transitionend', () => { resolve(); }); }); await raf(); detailsElement.querySelector('summary')!.click(); await transitioned; await assertScreenshot('explain/console_insight_references.png'); }); });