blob: b4ba3e1e79279099a0f8c3d69236c9af97104744 [file]
// Copyright 2026 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import {assert} from 'chai';
import * as Protocol from '../../generated/protocol.js';
import {setupLocaleHooks} from '../../testing/LocaleHelpers.js';
import {setupRuntimeHooks} from '../../testing/RuntimeHelpers.js';
import {setupSettingsHooks} from '../../testing/SettingsHelpers.js';
import * as SDK from './sdk.js';
describe('CSSStyleRule', () => {
setupLocaleHooks();
setupSettingsHooks();
setupRuntimeHooks();
describe('constructResolvedSelector', () => {
function createMockRule(selectorText: string, nestingSelectors?: string[]): SDK.CSSRule.CSSStyleRule {
return new SDK.CSSRule.CSSStyleRule({} as SDK.CSSModel.CSSModel, {
origin: Protocol.CSS.StyleSheetOrigin.Regular,
selectorList: {selectors: [{text: selectorText}], text: selectorText},
nestingSelectors,
style: {cssProperties: [], shorthandEntries: []},
});
}
it('returns the selector unchanged when there are no nesting selectors', () => {
assert.strictEqual(
createMockRule('.card').constructResolvedSelector(),
'.card',
);
assert.strictEqual(
createMockRule('.card', []).constructResolvedSelector(),
'.card',
);
});
it('resolves singly-nested selector with &', () => {
assert.strictEqual(
createMockRule('& .title', ['.card']).constructResolvedSelector(),
':is(.card) .title',
);
});
it('resolves singly-nested selector with direct child combinator', () => {
assert.strictEqual(
createMockRule('& > .child', ['.card']).constructResolvedSelector(),
':is(.card) > .child',
);
});
it('resolves doubly-nested selectors', () => {
const rule = createMockRule('& .title', ['& .card', '.container']);
assert.strictEqual(
rule.constructResolvedSelector(),
':is(:is(.container) .card) .title',
);
assert.strictEqual(
rule.constructResolvedSelector(0),
':is(.container) .card',
);
assert.strictEqual(
rule.constructResolvedSelector(1),
'.container',
);
});
it('resolves selectors with comma-separated parent selectors', () => {
assert.strictEqual(
createMockRule('& .title', ['.header, .sidebar']).constructResolvedSelector(),
':is(.header, .sidebar) .title',
);
});
it('resolves nested selectors without explicit &', () => {
assert.strictEqual(
createMockRule('.title', ['.card']).constructResolvedSelector(),
':is(.card) .title',
);
assert.strictEqual(
createMockRule('> .child', ['.card']).constructResolvedSelector(),
':is(.card) > .child',
);
});
it('handles pseudo-elements correctly', () => {
assert.strictEqual(
createMockRule('&::before', ['.card']).constructResolvedSelector(),
':is(.card)::before',
);
assert.strictEqual(
createMockRule('& .child', ['.card::before']).constructResolvedSelector(),
':is(.card) .child',
);
});
it('returns undefined for out-of-bounds nesting indices', () => {
const rule = createMockRule('& .title', ['.card']);
assert.isUndefined(rule.constructResolvedSelector(-1));
assert.isUndefined(rule.constructResolvedSelector(5));
});
});
});