mirror of
https://github.com/ChromeDevTools/chrome-devtools-mcp.git
synced 2026-09-28 19:32:57 +08:00
feat: update css formatter class to add AtRule, PositionTryRule, PropertyRule, FunctionRule (#2717)
Adds formatting support for modern CSS at-rules Co-authored-by: Samiya Caur <samiyac@chromium.org>
This commit is contained in:
co-authored by
Samiya Caur
parent
d05cbc0511
commit
d4a0620ce6
@@ -126,12 +126,53 @@ export interface PseudoElementRule {
|
||||
properties: StructuredCssProperty[];
|
||||
}
|
||||
|
||||
export interface AtRule {
|
||||
type: 'at-rule';
|
||||
atRuleType: string;
|
||||
name?: string;
|
||||
subsection?: string;
|
||||
selector: string;
|
||||
source?: string;
|
||||
ancestors?: AncestorCSSRule[];
|
||||
properties: StructuredCssProperty[];
|
||||
}
|
||||
|
||||
export interface PositionTryRule {
|
||||
type: 'position-try';
|
||||
name: string;
|
||||
active: boolean;
|
||||
selector: string;
|
||||
source?: string;
|
||||
ancestors?: AncestorCSSRule[];
|
||||
properties: StructuredCssProperty[];
|
||||
}
|
||||
|
||||
export interface PropertyRule {
|
||||
type: 'property';
|
||||
name: string;
|
||||
selector: string;
|
||||
source?: string;
|
||||
properties: StructuredCssProperty[];
|
||||
}
|
||||
|
||||
export interface FunctionRule {
|
||||
type: 'function';
|
||||
name: string;
|
||||
selector: string;
|
||||
source?: string;
|
||||
properties: StructuredCssProperty[];
|
||||
}
|
||||
|
||||
export type CascadeRule =
|
||||
| NodeStyleRule
|
||||
| AnimationRule
|
||||
| MatchedRule
|
||||
| InheritedRule
|
||||
| PseudoElementRule;
|
||||
| PseudoElementRule
|
||||
| AtRule
|
||||
| PositionTryRule
|
||||
| PropertyRule
|
||||
| FunctionRule;
|
||||
|
||||
export interface StructuredCssStyles {
|
||||
element: {
|
||||
@@ -470,6 +511,9 @@ function getCascadeRuleHeader(rule: CascadeRule): string {
|
||||
case 'animation':
|
||||
case 'attributes':
|
||||
case 'matched':
|
||||
case 'at-rule':
|
||||
case 'property':
|
||||
case 'function':
|
||||
selector = rule.selector;
|
||||
break;
|
||||
case 'inherited':
|
||||
@@ -478,6 +522,11 @@ function getCascadeRuleHeader(rule: CascadeRule): string {
|
||||
case 'pseudo':
|
||||
selector = rule.selector ?? rule.pseudoType;
|
||||
break;
|
||||
case 'position-try': {
|
||||
const statePrefix = rule.active ? '' : '[inactive] ';
|
||||
selector = `${statePrefix}${rule.selector}`;
|
||||
break;
|
||||
}
|
||||
}
|
||||
const source = 'source' in rule ? rule.source : undefined;
|
||||
return source ? `${selector} (${source})` : selector;
|
||||
@@ -605,6 +654,10 @@ export class CssFormatter {
|
||||
const rules: CascadeRule[] = [];
|
||||
CssFormatter.#collectNodeStyles(rules, matchedStyles, options);
|
||||
CssFormatter.#collectPseudoStyles(rules, matchedStyles, options);
|
||||
CssFormatter.#collectAtRules(rules, matchedStyles);
|
||||
CssFormatter.#collectPositionTryRules(rules, matchedStyles);
|
||||
CssFormatter.#collectRegisteredProperties(rules, matchedStyles);
|
||||
CssFormatter.#collectFunctionRules(rules, matchedStyles);
|
||||
return rules;
|
||||
}
|
||||
|
||||
@@ -834,6 +887,128 @@ export class CssFormatter {
|
||||
}
|
||||
}
|
||||
|
||||
static #collectAtRules(
|
||||
rules: CascadeRule[],
|
||||
matchedStyles: MatchedStyles,
|
||||
): void {
|
||||
const atRules = matchedStyles.atRules?.() ?? [];
|
||||
for (const atRule of atRules) {
|
||||
const properties = CssFormatter.#getStyleProperties(atRule.style);
|
||||
if (!properties.length) {
|
||||
continue;
|
||||
}
|
||||
const subsection = atRule.subsection() ?? undefined;
|
||||
const name = atRule.name()?.text;
|
||||
const type = atRule.type();
|
||||
const selector = subsection
|
||||
? `@${subsection}`
|
||||
: name
|
||||
? `@${type} ${name}`
|
||||
: `@${type}`;
|
||||
|
||||
const ancestors: AncestorCSSRule[] = [];
|
||||
if (subsection) {
|
||||
ancestors.push({
|
||||
type: 'at-rule',
|
||||
atRuleType: type,
|
||||
...(name ? {name} : {}),
|
||||
});
|
||||
}
|
||||
|
||||
rules.push({
|
||||
type: 'at-rule',
|
||||
atRuleType: type,
|
||||
...(name ? {name} : {}),
|
||||
...(subsection ? {subsection} : {}),
|
||||
selector,
|
||||
...(ancestors.length > 0 ? {ancestors} : {}),
|
||||
source: getSourceLocation(atRule),
|
||||
properties: CssFormatter.#formatProperties(properties, matchedStyles),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static #collectPositionTryRules(
|
||||
rules: CascadeRule[],
|
||||
matchedStyles: MatchedStyles,
|
||||
): void {
|
||||
const positionTryRules = matchedStyles.positionTryRules?.() ?? [];
|
||||
for (const positionTryRule of positionTryRules) {
|
||||
const properties = CssFormatter.#getStyleProperties(
|
||||
positionTryRule.style,
|
||||
);
|
||||
if (!properties.length) {
|
||||
continue;
|
||||
}
|
||||
const name = positionTryRule.name?.()?.text ?? '';
|
||||
const active = positionTryRule.active?.() ?? false;
|
||||
const source = getSourceLocation(positionTryRule);
|
||||
|
||||
rules.push({
|
||||
type: 'position-try',
|
||||
name,
|
||||
active,
|
||||
selector: `@position-try ${name}`,
|
||||
...(source ? {source} : {}),
|
||||
properties: CssFormatter.#formatProperties(properties, matchedStyles),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static #collectRegisteredProperties(
|
||||
rules: CascadeRule[],
|
||||
matchedStyles: MatchedStyles,
|
||||
): void {
|
||||
const registeredProperties = matchedStyles.registeredProperties?.() ?? [];
|
||||
for (const propertyRule of registeredProperties) {
|
||||
const style = propertyRule.style?.();
|
||||
if (!style) {
|
||||
continue;
|
||||
}
|
||||
const properties = CssFormatter.#getStyleProperties(style);
|
||||
if (!properties.length) {
|
||||
continue;
|
||||
}
|
||||
const name = propertyRule.propertyName?.() ?? '';
|
||||
const parentRule = style.parentRule;
|
||||
const source = parentRule
|
||||
? getSourceLocation(parentRule)
|
||||
: 'CSS.registerProperty';
|
||||
|
||||
rules.push({
|
||||
type: 'property',
|
||||
name,
|
||||
selector: `@property ${name}`,
|
||||
...(source ? {source} : {}),
|
||||
properties: CssFormatter.#formatProperties(properties, matchedStyles),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static #collectFunctionRules(
|
||||
rules: CascadeRule[],
|
||||
matchedStyles: MatchedStyles,
|
||||
): void {
|
||||
const functionRules = matchedStyles.functionRules?.() ?? [];
|
||||
for (const functionRule of functionRules) {
|
||||
const properties = CssFormatter.#getStyleProperties(functionRule.style);
|
||||
if (!properties.length) {
|
||||
continue;
|
||||
}
|
||||
const name = functionRule.functionName?.()?.text ?? '';
|
||||
const nameWithParameters = functionRule.nameWithParameters?.() || name;
|
||||
const source = getSourceLocation(functionRule);
|
||||
|
||||
rules.push({
|
||||
type: 'function',
|
||||
name,
|
||||
selector: `@function ${nameWithParameters}`,
|
||||
...(source ? {source} : {}),
|
||||
properties: CssFormatter.#formatProperties(properties, matchedStyles),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static #formatProperties(
|
||||
props: DevTools.CSSProperty.CSSProperty[],
|
||||
matchedStyles: MatchedStyles,
|
||||
|
||||
@@ -1,3 +1,112 @@
|
||||
exports[`CssFormatter > formats @font-face at-rule without name toJSON 1`] = `
|
||||
{
|
||||
"element": {
|
||||
"uid": "elem-at-2",
|
||||
"selector": "button"
|
||||
},
|
||||
"rules": [
|
||||
{
|
||||
"type": "at-rule",
|
||||
"atRuleType": "font-face",
|
||||
"selector": "@font-face",
|
||||
"source": "<style>",
|
||||
"properties": [
|
||||
{
|
||||
"name": "font-family",
|
||||
"value": "Open Sans",
|
||||
"status": "active"
|
||||
},
|
||||
{
|
||||
"name": "src",
|
||||
"value": "url(font.woff2)",
|
||||
"status": "active"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`CssFormatter > formats @font-face at-rule without name toString 1`] = `
|
||||
Styles for button (uid: "elem-at-2"):
|
||||
|
||||
@font-face (<style>) {
|
||||
font-family: Open Sans;
|
||||
src: url(font.woff2);
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`CssFormatter > formats @font-palette-values at-rule with name toJSON 1`] = `
|
||||
{
|
||||
"element": {
|
||||
"uid": "elem-at-1",
|
||||
"selector": "button"
|
||||
},
|
||||
"rules": [
|
||||
{
|
||||
"type": "at-rule",
|
||||
"atRuleType": "font-palette-values",
|
||||
"name": "--my-palette",
|
||||
"selector": "@font-palette-values --my-palette",
|
||||
"source": "fonts.css:11",
|
||||
"properties": [
|
||||
{
|
||||
"name": "font-family",
|
||||
"value": "Bixa",
|
||||
"status": "active"
|
||||
},
|
||||
{
|
||||
"name": "base-palette",
|
||||
"value": "3",
|
||||
"status": "active"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`CssFormatter > formats @font-palette-values at-rule with name toString 1`] = `
|
||||
Styles for button (uid: "elem-at-1"):
|
||||
|
||||
@font-palette-values --my-palette (fonts.css:11) {
|
||||
font-family: Bixa;
|
||||
base-palette: 3;
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`CssFormatter > formats @function custom function rule with parameters and declarations toJSON 1`] = `
|
||||
{
|
||||
"element": {
|
||||
"uid": "elem-func",
|
||||
"selector": "button"
|
||||
},
|
||||
"rules": [
|
||||
{
|
||||
"type": "function",
|
||||
"name": "--double",
|
||||
"selector": "@function --double(--x)",
|
||||
"source": "math.css:5",
|
||||
"properties": [
|
||||
{
|
||||
"name": "result",
|
||||
"value": "calc(var(--x) * 2)",
|
||||
"status": "active"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`CssFormatter > formats @function custom function rule with parameters and declarations toString 1`] = `
|
||||
Styles for button (uid: "elem-func"):
|
||||
|
||||
@function --double(--x) (math.css:5) {
|
||||
result: calc(var(--x) * 2);
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`CssFormatter > formats @layer, @media, @supports, and @starting-style ancestor rules toJSON 1`] = `
|
||||
{
|
||||
"element": {
|
||||
@@ -93,6 +202,79 @@ Styles for button (uid: "elem-nav"):
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`CssFormatter > formats @property rules defined in stylesheets and programmatically toJSON 1`] = `
|
||||
{
|
||||
"element": {
|
||||
"uid": "elem-prop",
|
||||
"selector": "button"
|
||||
},
|
||||
"rules": [
|
||||
{
|
||||
"type": "property",
|
||||
"name": "--brand-color",
|
||||
"selector": "@property --brand-color",
|
||||
"source": "theme.css:11",
|
||||
"properties": [
|
||||
{
|
||||
"name": "syntax",
|
||||
"value": "\\"<color>\\"",
|
||||
"status": "active"
|
||||
},
|
||||
{
|
||||
"name": "inherits",
|
||||
"value": "false",
|
||||
"status": "active"
|
||||
},
|
||||
{
|
||||
"name": "initial-value",
|
||||
"value": "#1a73e8",
|
||||
"status": "active"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "property",
|
||||
"name": "--runtime-var",
|
||||
"selector": "@property --runtime-var",
|
||||
"source": "CSS.registerProperty",
|
||||
"properties": [
|
||||
{
|
||||
"name": "syntax",
|
||||
"value": "\\"<length>\\"",
|
||||
"status": "active"
|
||||
},
|
||||
{
|
||||
"name": "inherits",
|
||||
"value": "true",
|
||||
"status": "active"
|
||||
},
|
||||
{
|
||||
"name": "initial-value",
|
||||
"value": "10px",
|
||||
"status": "active"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`CssFormatter > formats @property rules defined in stylesheets and programmatically toString 1`] = `
|
||||
Styles for button (uid: "elem-prop"):
|
||||
|
||||
@property --brand-color (theme.css:11) {
|
||||
syntax: "<color>";
|
||||
inherits: false;
|
||||
initial-value: #1a73e8;
|
||||
}
|
||||
|
||||
@property --runtime-var (CSS.registerProperty) {
|
||||
syntax: "<length>";
|
||||
inherits: true;
|
||||
initial-value: 10px;
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`CssFormatter > formats @scope ancestor rule toJSON 1`] = `
|
||||
{
|
||||
"element": {
|
||||
@@ -132,6 +314,90 @@ Styles for button (uid: "item-1"):
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`CssFormatter > formats active and inactive @position-try rules toJSON 1`] = `
|
||||
{
|
||||
"element": {
|
||||
"uid": "elem-pos",
|
||||
"selector": "button"
|
||||
},
|
||||
"rules": [
|
||||
{
|
||||
"type": "position-try",
|
||||
"name": "--bottom",
|
||||
"active": true,
|
||||
"selector": "@position-try --bottom",
|
||||
"source": "anchor.css:21",
|
||||
"properties": [
|
||||
{
|
||||
"name": "top",
|
||||
"value": "anchor(bottom)",
|
||||
"status": "active"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "position-try",
|
||||
"name": "--top",
|
||||
"active": false,
|
||||
"selector": "@position-try --top",
|
||||
"source": "anchor.css:26",
|
||||
"properties": [
|
||||
{
|
||||
"name": "bottom",
|
||||
"value": "anchor(top)",
|
||||
"status": "active"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`CssFormatter > formats active and inactive @position-try rules toString 1`] = `
|
||||
Styles for button (uid: "elem-pos"):
|
||||
|
||||
@position-try --bottom (anchor.css:21) {
|
||||
top: anchor(bottom);
|
||||
}
|
||||
|
||||
[inactive] @position-try --top (anchor.css:26) {
|
||||
bottom: anchor(top);
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`CssFormatter > formats at-rule when present in atRules toJSON 1`] = `
|
||||
{
|
||||
"element": {
|
||||
"uid": "elem-at-4",
|
||||
"selector": "button"
|
||||
},
|
||||
"rules": [
|
||||
{
|
||||
"type": "at-rule",
|
||||
"atRuleType": "counter-style",
|
||||
"name": "thumbs",
|
||||
"selector": "@counter-style thumbs",
|
||||
"source": "<style>",
|
||||
"properties": [
|
||||
{
|
||||
"name": "system",
|
||||
"value": "cyclic",
|
||||
"status": "active"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`CssFormatter > formats at-rule when present in atRules toString 1`] = `
|
||||
Styles for button (uid: "elem-at-4"):
|
||||
|
||||
@counter-style thumbs (<style>) {
|
||||
system: cyclic;
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`CssFormatter > formats constructed stylesheets with and without sourceURL pragma toJSON 1`] = `
|
||||
{
|
||||
"element": {
|
||||
|
||||
@@ -15,12 +15,16 @@ import {
|
||||
} from '../../src/formatters/CssFormatter.js';
|
||||
import {DevTools} from '../../src/third_party/index.js';
|
||||
import {
|
||||
createMockCSSAtRule,
|
||||
createMockCSSInlineStyle,
|
||||
createMockCSSMatchedStyles,
|
||||
createMockCSSProperty,
|
||||
createMockCSSStyleDeclaration,
|
||||
createMockDOMNode,
|
||||
createMockCSSStyleRule,
|
||||
createMockCSSPositionTryRule,
|
||||
createMockCSSRegisteredProperty,
|
||||
createMockCSSFunctionRule,
|
||||
} from '../mocks.js';
|
||||
|
||||
describe('CssFormatter', () => {
|
||||
@@ -518,4 +522,104 @@ describe('CssFormatter', () => {
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
formatterTest('formats @font-palette-values at-rule with name', () => {
|
||||
const atRule = createMockCSSAtRule('font-palette-values', {
|
||||
name: '--my-palette',
|
||||
properties: [
|
||||
createMockCSSProperty('font-family', 'Bixa'),
|
||||
createMockCSSProperty('base-palette', '3'),
|
||||
],
|
||||
sourceURL: 'https://example.com/fonts.css',
|
||||
range: {startLine: 10, startColumn: 0, endLine: 14, endColumn: 1},
|
||||
});
|
||||
const matchedStyles = createMockCSSMatchedStyles({atRules: [atRule]});
|
||||
return new CssFormatter(matchedStyles, {uid: 'elem-at-1'});
|
||||
});
|
||||
|
||||
formatterTest('formats @font-face at-rule without name', () => {
|
||||
const atRule = createMockCSSAtRule('font-face', {
|
||||
properties: [
|
||||
createMockCSSProperty('font-family', 'Open Sans'),
|
||||
createMockCSSProperty('src', 'url(font.woff2)'),
|
||||
],
|
||||
});
|
||||
const matchedStyles = createMockCSSMatchedStyles({atRules: [atRule]});
|
||||
return new CssFormatter(matchedStyles, {uid: 'elem-at-2'});
|
||||
});
|
||||
|
||||
formatterTest('formats at-rule when present in atRules', () => {
|
||||
const atRule = createMockCSSAtRule('counter-style', {
|
||||
name: 'thumbs',
|
||||
properties: [createMockCSSProperty('system', 'cyclic')],
|
||||
});
|
||||
const matchedStyles = createMockCSSMatchedStyles({
|
||||
atRules: [atRule],
|
||||
});
|
||||
return new CssFormatter(matchedStyles, {uid: 'elem-at-4'});
|
||||
});
|
||||
|
||||
formatterTest('formats active and inactive @position-try rules', () => {
|
||||
const posActive = createMockCSSPositionTryRule('--bottom', {
|
||||
active: true,
|
||||
properties: [createMockCSSProperty('top', 'anchor(bottom)')],
|
||||
sourceURL: 'anchor.css',
|
||||
range: {startLine: 20, startColumn: 0, endLine: 22, endColumn: 1},
|
||||
});
|
||||
const posInactive = createMockCSSPositionTryRule('--top', {
|
||||
active: false,
|
||||
properties: [createMockCSSProperty('bottom', 'anchor(top)')],
|
||||
sourceURL: 'anchor.css',
|
||||
range: {startLine: 25, startColumn: 0, endLine: 27, endColumn: 1},
|
||||
});
|
||||
const matchedStyles = createMockCSSMatchedStyles({
|
||||
positionTryRules: [posActive, posInactive],
|
||||
});
|
||||
|
||||
return new CssFormatter(matchedStyles, {uid: 'elem-pos'});
|
||||
});
|
||||
|
||||
formatterTest(
|
||||
'formats @property rules defined in stylesheets and programmatically',
|
||||
() => {
|
||||
const propStylesheet = createMockCSSRegisteredProperty('--brand-color', {
|
||||
syntax: '"<color>"',
|
||||
inherits: false,
|
||||
initialValue: '#1a73e8',
|
||||
sourceURL: 'theme.css',
|
||||
range: {startLine: 10, startColumn: 0, endLine: 14, endColumn: 1},
|
||||
});
|
||||
const propProgrammatic = createMockCSSRegisteredProperty(
|
||||
'--runtime-var',
|
||||
{
|
||||
syntax: '"<length>"',
|
||||
inherits: true,
|
||||
initialValue: '10px',
|
||||
isProgrammatic: true,
|
||||
},
|
||||
);
|
||||
const matchedStyles = createMockCSSMatchedStyles({
|
||||
registeredProperties: [propStylesheet, propProgrammatic],
|
||||
});
|
||||
|
||||
return new CssFormatter(matchedStyles, {uid: 'elem-prop'});
|
||||
},
|
||||
);
|
||||
|
||||
formatterTest(
|
||||
'formats @function custom function rule with parameters and declarations',
|
||||
() => {
|
||||
const funcRule = createMockCSSFunctionRule('--double(--x)', {
|
||||
functionName: '--double',
|
||||
properties: [createMockCSSProperty('result', 'calc(var(--x) * 2)')],
|
||||
sourceURL: 'math.css',
|
||||
range: {startLine: 4, startColumn: 0, endLine: 6, endColumn: 1},
|
||||
});
|
||||
const matchedStyles = createMockCSSMatchedStyles({
|
||||
functionRules: [funcRule],
|
||||
});
|
||||
|
||||
return new CssFormatter(matchedStyles, {uid: 'elem-func'});
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
+153
@@ -45,6 +45,14 @@ export type MockCSSMatchedStyles =
|
||||
sinon.SinonStubbedInstance<DevTools.CSSMatchedStyles.CSSMatchedStyles>;
|
||||
export type MockCSSStyleRule =
|
||||
sinon.SinonStubbedInstance<DevTools.CSSRule.CSSStyleRule>;
|
||||
export type MockCSSAtRule =
|
||||
sinon.SinonStubbedInstance<DevTools.CSSRule.CSSAtRule>;
|
||||
export type MockCSSPositionTryRule =
|
||||
sinon.SinonStubbedInstance<DevTools.CSSRule.CSSPositionTryRule>;
|
||||
export type MockCSSRegisteredProperty =
|
||||
sinon.SinonStubbedInstance<DevTools.CSSMatchedStyles.CSSRegisteredProperty>;
|
||||
export type MockCSSFunctionRule =
|
||||
sinon.SinonStubbedInstance<DevTools.CSSRule.CSSFunctionRule>;
|
||||
|
||||
/**
|
||||
* A minimal event emitter used to back mocked `on`/`off`/`emit` methods on
|
||||
@@ -375,10 +383,151 @@ export function createMockCSSStyleRule(
|
||||
return rule;
|
||||
}
|
||||
|
||||
export function createMockCSSAtRule(
|
||||
type: string,
|
||||
options: {
|
||||
name?: string;
|
||||
subsection?: string;
|
||||
properties: DevTools.CSSProperty.CSSProperty[];
|
||||
sourceURL?: string;
|
||||
origin?: RuleOrigin;
|
||||
range?: {
|
||||
startLine: number;
|
||||
startColumn: number;
|
||||
endLine: number;
|
||||
endColumn: number;
|
||||
};
|
||||
},
|
||||
): MockCSSAtRule {
|
||||
const rule = sinon.createStubInstance(DevTools.CSSRule.CSSAtRule);
|
||||
attachRuleMeta(rule, options.sourceURL, options.origin);
|
||||
const style = createMockCSSStyleDeclaration(options.properties, {
|
||||
rule,
|
||||
range: options.range,
|
||||
});
|
||||
rule.type.returns(type);
|
||||
rule.name.returns(options.name ? createCSSValue(options.name) : null);
|
||||
rule.subsection.returns(options.subsection ?? null);
|
||||
Object.assign(rule, {style});
|
||||
return rule;
|
||||
}
|
||||
|
||||
export function createMockCSSPositionTryRule(
|
||||
name: string,
|
||||
options: {
|
||||
active?: boolean;
|
||||
properties: DevTools.CSSProperty.CSSProperty[];
|
||||
sourceURL?: string;
|
||||
range?: {
|
||||
startLine: number;
|
||||
startColumn: number;
|
||||
endLine: number;
|
||||
endColumn: number;
|
||||
};
|
||||
},
|
||||
): MockCSSPositionTryRule {
|
||||
const rule = sinon.createStubInstance(DevTools.CSSRule.CSSPositionTryRule);
|
||||
attachRuleMeta(rule, options.sourceURL);
|
||||
const style = createMockCSSStyleDeclaration(options.properties, {
|
||||
rule,
|
||||
range: options.range,
|
||||
});
|
||||
rule.name.returns(createCSSValue(name));
|
||||
rule.active.returns(options.active ?? false);
|
||||
Object.assign(rule, {style});
|
||||
return rule;
|
||||
}
|
||||
|
||||
export function createMockCSSRegisteredProperty(
|
||||
name: string,
|
||||
options: {
|
||||
syntax?: string;
|
||||
inherits?: boolean;
|
||||
initialValue?: string;
|
||||
sourceURL?: string;
|
||||
range?: {
|
||||
startLine: number;
|
||||
startColumn: number;
|
||||
endLine: number;
|
||||
endColumn: number;
|
||||
};
|
||||
isProgrammatic?: boolean;
|
||||
} = {},
|
||||
): MockCSSRegisteredProperty {
|
||||
const properties = [
|
||||
createMockCSSProperty('syntax', options.syntax ?? '"*"'),
|
||||
createMockCSSProperty('inherits', String(options.inherits ?? false)),
|
||||
];
|
||||
if (options.initialValue) {
|
||||
properties.push(
|
||||
createMockCSSProperty('initial-value', options.initialValue),
|
||||
);
|
||||
}
|
||||
|
||||
let parentRule: sinon.SinonStubbedInstance<DevTools.CSSRule.CSSPropertyRule> | null =
|
||||
null;
|
||||
if (!options.isProgrammatic) {
|
||||
const mockRule = sinon.createStubInstance(DevTools.CSSRule.CSSPropertyRule);
|
||||
attachRuleMeta(mockRule, options.sourceURL);
|
||||
mockRule.propertyName.returns(createCSSValue(name));
|
||||
parentRule = mockRule;
|
||||
}
|
||||
|
||||
const style = createMockCSSStyleDeclaration(properties, {
|
||||
rule: parentRule,
|
||||
range: options.range,
|
||||
});
|
||||
if (parentRule) {
|
||||
Object.assign(parentRule, {style});
|
||||
}
|
||||
|
||||
const prop = sinon.createStubInstance(
|
||||
DevTools.CSSMatchedStyles.CSSRegisteredProperty,
|
||||
);
|
||||
prop.propertyName.returns(name);
|
||||
prop.inherits.returns(options.inherits ?? false);
|
||||
prop.syntax.returns(options.syntax ?? '"*"');
|
||||
prop.initialValue.returns(options.initialValue ?? null);
|
||||
prop.style.returns(style);
|
||||
return prop;
|
||||
}
|
||||
|
||||
export function createMockCSSFunctionRule(
|
||||
nameWithParams: string,
|
||||
options: {
|
||||
functionName?: string;
|
||||
properties: DevTools.CSSProperty.CSSProperty[];
|
||||
sourceURL?: string;
|
||||
range?: {
|
||||
startLine: number;
|
||||
startColumn: number;
|
||||
endLine: number;
|
||||
endColumn: number;
|
||||
};
|
||||
},
|
||||
): MockCSSFunctionRule {
|
||||
const rule = sinon.createStubInstance(DevTools.CSSRule.CSSFunctionRule);
|
||||
attachRuleMeta(rule, options.sourceURL);
|
||||
const style = createMockCSSStyleDeclaration(options.properties, {
|
||||
rule,
|
||||
range: options.range,
|
||||
});
|
||||
const baseName =
|
||||
options.functionName ?? nameWithParams.split('(')[0] ?? nameWithParams;
|
||||
rule.functionName.returns(createCSSValue(baseName));
|
||||
rule.nameWithParameters.returns(nameWithParams);
|
||||
Object.assign(rule, {style});
|
||||
return rule;
|
||||
}
|
||||
|
||||
export interface MockCSSMatchedStylesParams {
|
||||
node?: string | DevTools.DOMModel.DOMNode;
|
||||
nodeStyles?: DevTools.CSSStyleDeclaration.CSSStyleDeclaration[];
|
||||
inheritedStyles?: DevTools.CSSStyleDeclaration.CSSStyleDeclaration[];
|
||||
atRules?: DevTools.CSSRule.CSSAtRule[];
|
||||
positionTryRules?: DevTools.CSSRule.CSSPositionTryRule[];
|
||||
registeredProperties?: DevTools.CSSMatchedStyles.CSSRegisteredProperty[];
|
||||
functionRules?: DevTools.CSSRule.CSSFunctionRule[];
|
||||
parentNode?: string | DevTools.DOMModel.DOMNode;
|
||||
nodeForStyleMap?: Map<
|
||||
DevTools.CSSStyleDeclaration.CSSStyleDeclaration,
|
||||
@@ -427,6 +576,10 @@ export function createMockCSSMatchedStyles(
|
||||
mock.node.returns(mockNode);
|
||||
mock.nodeStyles.returns(nodeStyles);
|
||||
mock.inheritedStyles.returns(inheritedStyles);
|
||||
mock.atRules.returns(params.atRules ?? []);
|
||||
mock.positionTryRules.returns(params.positionTryRules ?? []);
|
||||
mock.registeredProperties.returns(params.registeredProperties ?? []);
|
||||
mock.functionRules.returns(params.functionRules ?? []);
|
||||
mock.pseudoTypes.returns(pseudoTypes);
|
||||
mock.customHighlightPseudoNames.returns(new Set(customHighlights.keys()));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user