blob: 203cd1116cf47011d02ef3e3986305bc188514fd [file] [log] [blame] [edit]
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import '../../test/common-test-setup';
import {assert, fixture, html} from '@open-wc/testing';
import {ContextItem} from '../../api/ai-code-review';
import sinon from 'sinon';
import './context-chip';
import {ContextChip} from './context-chip';
import {testResolver} from '../../test/common-test-setup';
import {pluginLoaderToken} from '../shared/gr-js-api-interface/gr-plugin-loader';
import {chatProvider, createChange} from '../../test/test-data-generators';
import {changeModelToken} from '../../models/change/change-model';
import {ParsedChangeInfo} from '../../types/types';
import {MdFilterChip} from '@material/web/chips/filter-chip';
suite('context-chip tests', () => {
let element: ContextChip;
setup(async () => {
const pluginLoader = testResolver(pluginLoaderToken);
pluginLoader.pluginsModel.aiCodeReviewRegister({
pluginName: 'test-plugin',
provider: chatProvider,
});
const changeModel = testResolver(changeModelToken);
changeModel.updateState({
change: createChange() as ParsedChangeInfo,
});
element = await fixture(html`<context-chip></context-chip>`);
});
test('renders with default properties', () => {
assert.shadowDom.equal(
element,
/* HTML */ `
<md-filter-chip class="context-chip no-link" removable="" title="">
<div class="context-chip-container">
<span class="context-chip-title"> </span>
</div>
</md-filter-chip>
`
);
});
test('renders with text', async () => {
element.text = 'test text';
await element.updateComplete;
const chip = element.shadowRoot?.querySelector('md-filter-chip');
assert.equal(chip?.textContent?.trim(), 'test text');
});
test('renders with subtext', async () => {
element.subText = 'sub text';
await element.updateComplete;
const subtext = element.shadowRoot?.querySelector('.subtext');
assert.isOk(subtext);
assert.dom.equal(subtext, '<span class="subtext">: sub text</span>');
});
test('renders as suggestion', async () => {
element.isSuggestion = true;
await element.updateComplete;
const icon = element.shadowRoot?.querySelector('gr-icon');
assert.isOk(icon);
assert.equal(icon.getAttribute('icon'), 'add');
});
test('renders as custom action', async () => {
element.isCustomAction = true;
await element.updateComplete;
const chip = element.shadowRoot?.querySelector('md-filter-chip');
assert.isTrue(chip?.classList.contains('custom-action-chip'));
});
test('renders with tooltip', async () => {
element.tooltip = 'test tooltip';
await element.updateComplete;
const chip = element.shadowRoot?.querySelector('md-filter-chip');
assert.equal(chip?.title, 'test tooltip');
});
test('is removable', async () => {
element.isRemovable = true;
element.isSuggestion = false;
await element.updateComplete;
const chip = element.shadowRoot?.querySelector('md-filter-chip');
assert.isTrue(chip?.removable);
});
test('is not removable when suggestion', async () => {
element.isRemovable = true;
element.isSuggestion = true;
await element.updateComplete;
const chip = element.shadowRoot?.querySelector('md-filter-chip');
assert.isFalse(chip?.removable);
});
test('fires remove-context-chip event', async () => {
const spy = sinon.spy();
element.addEventListener('remove-context-chip', spy);
const chip = element.shadowRoot?.querySelector('md-filter-chip');
chip?.dispatchEvent(new Event('remove'));
assert.isTrue(spy.called);
});
test('fires accept-context-item-suggestion event on chip click', async () => {
element.isSuggestion = true;
await element.updateComplete;
const spy = sinon.spy();
element.addEventListener('accept-context-item-suggestion', spy);
const chip = element.shadowRoot?.querySelector('md-filter-chip');
chip?.click();
assert.isTrue(spy.called);
});
test('navigates to url', async () => {
const openSpy = sinon.spy(window, 'open');
const contextItem: ContextItem = {
type_id: 'file',
title: 'test.ts',
link: ' gerrit.test/test.ts ',
};
element.contextItem = contextItem;
await element.updateComplete;
const chip = element.shadowRoot?.querySelector('md-filter-chip');
chip?.click();
assert.isTrue(openSpy.calledWith('http://gerrit.test/test.ts', '_blank'));
});
test('has no-link class when no link', async () => {
const contextItem: ContextItem = {
type_id: 'file',
title: 'test.ts',
link: '',
};
element.contextItem = contextItem;
await element.updateComplete;
const chip = element.shadowRoot?.querySelector('md-filter-chip');
assert.isTrue(chip?.classList.contains('no-link'));
});
test('does not have no-link class when has link', async () => {
const contextItem: ContextItem = {
type_id: 'file',
title: 'test.ts',
link: ' http://gerrit.test/test.ts ',
};
element.contextItem = contextItem;
await element.updateComplete;
const chip = element.shadowRoot?.querySelector('md-filter-chip');
assert.isFalse(chip?.classList.contains('no-link'));
});
test('does not navigate on click when no link', async () => {
const openSpy = sinon.spy(window, 'open');
const contextItem: ContextItem = {
type_id: 'file',
title: 'test.ts',
link: '',
};
element.contextItem = contextItem;
await element.updateComplete;
const chip = element.shadowRoot?.querySelector('md-filter-chip');
chip?.click();
assert.isFalse(openSpy.called);
});
test('shortens long context item titles', async () => {
const contextItem: ContextItem = {
type_id: 'file',
title: 'very/long/path/to/some/deeply/nested/file.ts',
link: '...',
};
element.contextItem = contextItem;
await element.updateComplete;
const chip =
element.shadowRoot?.querySelector<MdFilterChip>('md-filter-chip');
assert.equal(chip?.textContent?.trim(), '\u2026/nested/file.ts');
assert.equal(chip?.title, 'very/long/path/to/some/deeply/nested/file.ts');
});
test('does not shorten short context item titles', async () => {
const contextItem: ContextItem = {
type_id: 'file',
title: 'short/file.ts',
link: '...',
};
element.contextItem = contextItem;
await element.updateComplete;
const chip =
element.shadowRoot?.querySelector<MdFilterChip>('md-filter-chip');
assert.equal(chip?.textContent?.trim(), 'short/file.ts');
});
});