blob: 652c8ad49b585c4c9ba197fc1d507005dac36cdf [file] [log] [blame]
// 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 Host from '../../../core/host/host.js';
import {dispatchClickEvent, renderElementIntoDOM} from '../../../testing/DOMHelpers.js';
import {describeWithLocale} from '../../../testing/EnvironmentHelpers.js';
import type * as Marked from '../../../third_party/marked/marked.js';
import * as Explain from '../explain.js';
const {assert} = chai;
describeWithLocale('ConsoleInsight', () => {
describe('Markdown renderer', () => {
it('renders link as an x-link', () => {
const renderer = new Explain.MarkdownRenderer();
const result =
renderer.renderToken({type: 'link', text: 'learn more', href: 'exampleLink'} as Marked.Marked.Token);
assert((result.values[0] as HTMLElement).tagName === 'X-LINK');
});
it('renders images as an x-link', () => {
const renderer = new Explain.MarkdownRenderer();
const result =
renderer.renderToken({type: 'image', text: 'learn more', href: 'exampleLink'} as Marked.Marked.Token);
assert((result.values[0] as HTMLElement).tagName === 'X-LINK');
});
it('renders headers as a strong element', () => {
const renderer = new Explain.MarkdownRenderer();
const result = renderer.renderToken({type: 'heading', text: 'learn more'} as Marked.Marked.Token);
assert(result.strings.join('').includes('<strong>'));
});
it('renders unsupported tokens', () => {
const renderer = new Explain.MarkdownRenderer();
const result = renderer.renderToken({type: 'html', raw: '<!DOCTYPE html>'} as Marked.Marked.Token);
assert(result.values.join('').includes('<!DOCTYPE html>'));
});
});
describe('ConsoleInsight', () => {
function getTestAidaClient() {
return {
async *
fetch() {
yield {explanation: 'test', metadata: {}};
},
};
}
function getTestPromptBuilder() {
return {
async buildPrompt() {
return {
prompt: '',
sources: [
{
type: Explain.SourceType.MESSAGE,
value: 'error message',
},
],
};
},
};
}
async function drainMicroTasks() {
await new Promise(resolve => setTimeout(resolve, 0));
}
it('shows the consent flow for signed-in users', async () => {
const component = new Explain.ConsoleInsight(getTestPromptBuilder(), getTestAidaClient(), '', {
isSyncActive: true,
accountEmail: 'some-email',
});
renderElementIntoDOM(component);
await drainMicroTasks();
// Consent button is present.
assert(component.shadowRoot!.querySelector('.consent-button'));
});
it('consent can be accepted', async () => {
const component = new Explain.ConsoleInsight(getTestPromptBuilder(), getTestAidaClient(), '', {
isSyncActive: true,
accountEmail: 'some-email',
});
renderElementIntoDOM(component);
await drainMicroTasks();
dispatchClickEvent(component.shadowRoot!.querySelector('.consent-button')!, {
bubbles: true,
composed: true,
});
// Expected to be rendered in the next task.
await new Promise(resolve => setTimeout(resolve, 0));
// Rating buttons are shown.
assert(component.shadowRoot!.querySelector('.rating'));
});
const reportsRating = (positive: boolean) => async () => {
const openInNewTab = sinon.stub(Host.InspectorFrontendHost.InspectorFrontendHostInstance, 'openInNewTab');
const actionTaken = sinon.stub(Host.userMetrics, 'actionTaken');
const registerAidaClientEvent =
sinon.stub(Host.InspectorFrontendHost.InspectorFrontendHostInstance, 'registerAidaClientEvent');
const component = new Explain.ConsoleInsight(getTestPromptBuilder(), getTestAidaClient(), '', {
isSyncActive: true,
accountEmail: 'some-email',
});
renderElementIntoDOM(component);
await drainMicroTasks();
dispatchClickEvent(component.shadowRoot!.querySelector('.consent-button')!, {
bubbles: true,
composed: true,
});
// Expected to be rendered in the next task.
await new Promise(resolve => setTimeout(resolve, 0));
dispatchClickEvent(component.shadowRoot!.querySelector(`.rating [data-rating=${positive}]`)!, {
bubbles: true,
composed: true,
});
assert(openInNewTab.calledOnce);
assert.include(openInNewTab.firstCall.firstArg, positive ? 'Positive' : 'Negative');
assert(registerAidaClientEvent.calledOnce);
assert.include(registerAidaClientEvent.firstCall.firstArg, positive ? 'POSITIVE' : 'NEGATIVE');
assert(actionTaken.calledWith(
positive ? Host.UserMetrics.Action.InsightRatedPositive : Host.UserMetrics.Action.InsightRatedNegative));
};
it('reports positive rating', reportsRating(true));
it('reports negative rating', reportsRating(false));
it('report if the user is not logged in', async () => {
const component = new Explain.ConsoleInsight(getTestPromptBuilder(), getTestAidaClient(), '', {
isSyncActive: false,
});
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 sync is not enabled', async () => {
const component = new Explain.ConsoleInsight(getTestPromptBuilder(), getTestAidaClient(), '', {
isSyncActive: false,
accountEmail: 'some-email',
});
renderElementIntoDOM(component);
await drainMicroTasks();
const content = component.shadowRoot!.querySelector('main')!.innerText.trim();
assert.strictEqual(content, 'This feature requires you to turn on Chrome sync.');
});
it('report if the navigator is offline', async () => {
const navigatorDescriptor = Object.getOwnPropertyDescriptor(globalThis, 'navigator')!;
Object.defineProperty(globalThis, 'navigator', {
get() {
return {onLine: false};
},
});
try {
const component = new Explain.ConsoleInsight(getTestPromptBuilder(), getTestAidaClient(), '', {
isSyncActive: false,
accountEmail: 'some-email',
});
renderElementIntoDOM(component);
await drainMicroTasks();
const content = component.shadowRoot!.querySelector('main')!.innerText.trim();
assert.strictEqual(content, 'Check your internet connection and try again.');
} finally {
Object.defineProperty(globalThis, 'navigator', navigatorDescriptor);
}
});
});
});