blob: 81a593a485d454d95ba172f9261653edada87cf0 [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import * as SDK from '../../core/sdk/sdk.js';
import {createTarget} from '../../testing/EnvironmentHelpers.js';
import {describeWithMockConnection} from '../../testing/MockConnection.js';
import {getMatchedStyles} from '../../testing/StyleHelpers.js';
import * as UI from '../../ui/legacy/legacy.js';
import * as PanelUtils from '../utils/utils.js';
import * as Elements from './elements.js';
describeWithMockConnection('StylePropertyHighlighter', () => {
async function setupStylesPane(): Promise<{
stylesSidebarPane: Elements.StylesSidebarPane.StylesSidebarPane,
matchedStyles: SDK.CSSMatchedStyles.CSSMatchedStyles,
}> {
const target = createTarget();
UI.Context.Context.instance().setFlavor(SDK.DOMModel.DOMNode, sinon.createStubInstance(SDK.DOMModel.DOMNode));
const computedStyleModel = new Elements.ComputedStyleModel.ComputedStyleModel();
const stylesSidebarPane = new Elements.StylesSidebarPane.StylesSidebarPane(computedStyleModel);
const matchedStyles = await getMatchedStyles(
{node: stylesSidebarPane.node() as SDK.DOMModel.DOMNode, cssModel: target.model(SDK.CSSModel.CSSModel)!});
return {
stylesSidebarPane,
matchedStyles,
};
}
function createSection(
stylesSidebarPane: Elements.StylesSidebarPane.StylesSidebarPane,
matchedStyles: SDK.CSSMatchedStyles.CSSMatchedStyles, sectionName?: string, propertyName?: string) {
const style = sinon.createStubInstance(SDK.CSSStyleDeclaration.CSSStyleDeclaration);
if (propertyName) {
const property =
new SDK.CSSProperty.CSSProperty(style, 0, propertyName, 'value', true, false, true, false, '', undefined);
style.leadingProperties.returns([property]);
style.hasActiveProperty.callsFake(name => name === propertyName);
} else {
style.leadingProperties.returns([]);
}
return new Elements.StylePropertiesSection.StylePropertiesSection(
stylesSidebarPane, matchedStyles, style,
/* sectionIdx */ 0, /* computedStyles */ null,
/* parentsComputedStyles */ null, sectionName);
}
function createBlockAndSection(
stylesSidebarPane: Elements.StylesSidebarPane.StylesSidebarPane,
matchedStyles: SDK.CSSMatchedStyles.CSSMatchedStyles, sectionName?: string, propertyName?: string) {
const titleElement = sinon.createStubInstance(Element);
const block = new Elements.StylesSidebarPane.SectionBlock(titleElement, true);
block.sections = [createSection(stylesSidebarPane, matchedStyles, sectionName, propertyName)];
return block;
}
it('highlights layers', async () => {
const {stylesSidebarPane, matchedStyles} = await setupStylesPane();
const getSectionBlockByName = sinon.stub(stylesSidebarPane, 'getSectionBlockByName');
const block = createBlockAndSection(stylesSidebarPane, matchedStyles);
getSectionBlockByName.returns(block);
const [section] = block.sections;
const style = section.style() as sinon.SinonStubbedInstance<SDK.CSSStyleDeclaration.CSSStyleDeclaration>;
// Attach a property late, in order to verify that highlighting the layer repopulates the tree view.
const property = new SDK.CSSProperty.CSSProperty(style, 0, '', '', true, false, true, false, '', undefined);
style.leadingProperties.returns([property]);
assert.isUndefined(section.propertiesTreeOutline.firstChild());
const highlighter = new Elements.StylePropertyHighlighter.StylePropertyHighlighter(stylesSidebarPane);
const highlightSpy = sinon.spy(PanelUtils.PanelUtils, 'highlightElement');
highlighter.findAndHighlightSectionBlock('dontcare');
const firstChild = section.propertiesTreeOutline.firstChild();
assert.exists(firstChild);
assert.deepEqual((firstChild as Elements.StylePropertyTreeElement.StylePropertyTreeElement).property, property);
sinon.assert.calledOnceWithExactly(highlightSpy, block.titleElement() as HTMLElement);
});
it('highlights sections', async () => {
const {stylesSidebarPane, matchedStyles} = await setupStylesPane();
const getSectionBlockByName = sinon.stub(stylesSidebarPane, 'getSectionBlockByName');
const block = createBlockAndSection(stylesSidebarPane, matchedStyles, 'sectionname');
const blockExpandSpy = sinon.spy(block, 'expand');
getSectionBlockByName.callsFake(name => name === 'blockname' ? block : undefined);
const highlighter = new Elements.StylePropertyHighlighter.StylePropertyHighlighter(stylesSidebarPane);
const highlightSpy = sinon.stub(PanelUtils.PanelUtils, 'highlightElement');
highlighter.findAndHighlightSection('sectionname', 'blockname');
sinon.assert.called(blockExpandSpy);
sinon.assert.calledOnceWithExactly(highlightSpy, block.sections[0].element);
});
it('highlights properties in sections in blocks', async () => {
const {stylesSidebarPane, matchedStyles} = await setupStylesPane();
const getSectionBlockByName = sinon.stub(stylesSidebarPane, 'getSectionBlockByName');
const block1 = createBlockAndSection(stylesSidebarPane, matchedStyles, 'section1', 'property');
const block1ExpandSpy = sinon.spy(block1, 'expand');
const block2 = createBlockAndSection(stylesSidebarPane, matchedStyles, 'section2', 'property');
block2.sections.unshift(createSection(stylesSidebarPane, matchedStyles, 'extrasection'));
const block2ExpandSpy = sinon.spy(block2, 'expand');
getSectionBlockByName.callsFake(name => {
if (name === 'block1') {
return block1;
}
if (name === 'block2') {
return block2;
}
return undefined;
});
const highlighter = new Elements.StylePropertyHighlighter.StylePropertyHighlighter(stylesSidebarPane);
const highlightSpy = sinon.stub(PanelUtils.PanelUtils, 'highlightElement');
highlighter.findAndHighlightPropertyName('property', 'section2', 'block2');
sinon.assert.notCalled(block1ExpandSpy);
sinon.assert.called(block2ExpandSpy);
const element = block2.sections[1].propertiesTreeOutline.firstChild()?.listItemElement;
assert.exists(element);
sinon.assert.calledOnceWithExactly(highlightSpy, element);
});
it('highlights longhand properties of a shorthand property', async () => {
const {stylesSidebarPane, matchedStyles} = await setupStylesPane();
const style = sinon.createStubInstance(SDK.CSSStyleDeclaration.CSSStyleDeclaration);
const shorthandProperty =
new SDK.CSSProperty.CSSProperty(style, 0, 'background', 'red', true, false, true, false, '', undefined);
const longhandProperty =
new SDK.CSSProperty.CSSProperty(style, 1, 'background-color', 'red', true, false, true, false, '', undefined);
sinon.stub(shorthandProperty, 'getLonghandProperties').returns([longhandProperty]);
style.leadingProperties.returns([shorthandProperty]);
style.allProperties.returns([shorthandProperty, longhandProperty]);
const section = new Elements.StylePropertiesSection.StylePropertiesSection(
stylesSidebarPane, matchedStyles, style, 0, null, null);
sinon.stub(stylesSidebarPane, 'allSections').returns([section]);
const highlighter = new Elements.StylePropertyHighlighter.StylePropertyHighlighter(stylesSidebarPane);
const highlightSpy = sinon.stub(PanelUtils.PanelUtils, 'highlightElement');
await highlighter.highlightProperty(longhandProperty);
// Assert that the shorthand is expanded and the longhand is highlighted.
const shorthandTreeElement =
section.propertiesTreeOutline.firstChild() as Elements.StylePropertyTreeElement.StylePropertyTreeElement;
const longhandTreeElement =
shorthandTreeElement.childAt(0) as Elements.StylePropertyTreeElement.StylePropertyTreeElement;
assert.isTrue(shorthandTreeElement.expanded, 'Shorthand property should be expanded');
sinon.assert.calledOnceWithExactly(highlightSpy, longhandTreeElement.listItemElement);
});
});