CSS特异性测试

6

有没有好的工具或方法可以自动测试CSS选择器?

我正在开发一个SCSS框架,并希望在其中包含自动化测试。具体来说,我想要测试以确保CSS选择器正常工作。

例如,假设我有以下HTML:

<input class="btn" disabled id="test"></input>

以及 CSS

.btn {
 color: red;
 ...
}

.btn:disabled {
 color: green;
 ...
}

我希望有一个测试,能够确保具有id =“test”的元素,拥有.btn:disabled作为最高优先级的css类(最后一个特定度最高的类),并且.btn是第二高优先级。换句话说,我想确保在该元素上应用.btn:disabled和.btn css样式,并且.btn:disabled中的样式覆盖.btn中的样式。
我考虑在Selenium中完成这个测试。有没有好的方法可以做到这一点?我不想在测试中硬编码CSS值。

1
“将.btn:disabled作为“primary”CSS类,而.btn作为“secondary”。” - 不存在“primary”和“secondary CSS类”。请解释您实际的意思。 - 04FS
@04FS,我现在已经重新表述了,应该更清楚了 :-) - Torstein I. Bø
@TorsteinI.Bø 你所说的“最高特异性”和“最高优先级”具体指什么? - undetected Selenium
所以在上面的示例中,<input>元素应用了两种样式。因此,浏览器需要决定使用哪一个。它使用具有最高特异性的样式(https://www.w3schools.com/css/css_specificity.asp)。但是,如果两个CSS样式具有相同的特异性,则使用最新的那个。因此,最终使用的样式是具有最高优先级的样式。在这个例子中,.btn:disabled的特异性为0,0,2,0,而.btn的特异性为0,0,1,0,因此.btn:disabled具有最高的特异性,因此被使用(具有最高的优先级)。 - Torstein I. Bø
1
你需要使用CSSOM来查找你的主要目标规则。将该属性值与元素的计算样式进行比较。断言它们是相同的。更改属性的规则为不同的值,然后再次进行比较。如果它们仍然相同,则您的规则是主要规则。修改规则选择器以使其不匹配。对于您想要测试的每个进一步的目标规则,从顶部重复此过程。所有这些都可以使用Selenium中的JavascriptExecutor实现。 - Alohci
显示剩余2条评论
4个回答

3
我采用的方法是使用getComputedStyle来获取“最高优先级”的样式。在CSS中,我将“标签”添加到内容属性中。然后在Jasmine中,我检查所需的标记是否为computedStyle。(我将在scss中扩展此功能,以便如果使用测试模式,则通过mixin设置内容属性,而不是在生产中设置内容属性。)这仅对具有最高优先级的类进行单元测试,但不适用于第二高等等的类别。
下面是一个测试,以说明此示例(只有第一个和最后一个测试应该通过)。

// specs code
describe("CSS", function() {
  it("Div element of class test should be handled by .test", () => {
    const testdiv = document.getElementById("testdiv")
    m = window.getComputedStyle(testdiv).getPropertyValue("content"); 
   
    expect(m).toEqual('".test"');
  });

 it("Div element of class test should be handled by div", () => {
    const testdiv = document.getElementById("testdiv")
    m = window.getComputedStyle(testdiv).getPropertyValue("content"); 
   
    expect(m).toEqual('"div"');
  });

 it("Div element should be handled by .test", () => {
    const testdiv = document.getElementById("testdiv2")
    m = window.getComputedStyle(testdiv).getPropertyValue("content"); 
   
    expect(m).toEqual('".test"');
  });

 it("Div element of class test should be handled by div", () => {
    const testdiv = document.getElementById("testdiv2")
    m = window.getComputedStyle(testdiv).getPropertyValue("content"); 
   
    expect(m).toEqual('"div"');
  });

});


// load jasmine htmlReporter
(function() {
  var env = jasmine.getEnv();
  env.addReporter(new jasmine.HtmlReporter());
  env.execute();
}());
.test {
    content: '.test';
}

div {
  content: 'div';
}
<script src="https://cdn.jsdelivr.net/jasmine/1.3.1/jasmine.js"></script>
<script src="https://cdn.jsdelivr.net/jasmine/1.3.1/jasmine-html.js"></script>
<link href="https://cdn.jsdelivr.net/jasmine/1.3.1/jasmine.css" rel="stylesheet"/>
<div class="test" id="testdiv">TestDiv</div>
<div id="testdiv2">TestDiv</div>


1
如果我理解问题正确,你基本上是在要求一个像这样的测试(针对你的示例):
if -> Element.ComputedStyle.color = green

then -> test passed

else -> test failed (you have CSS structure errors)

显然,浏览器会正确解释特异性。因此,您真正测试的是通过添加/覆盖CSS所做的更改是否导致了意外的视觉后果。

这似乎是一个相当手动的问题,需要您决定每个正确状态是什么,然后维护测试。如果您选择这条路线,我建议看看Backstop.js之类的东西。尽管CSS视觉回归测试非常复杂,所以请注意不要期望太多。


手动方式

你能否通过创建一个通常为 transparent 的 SCSS 变量来手动解决问题?然后在添加/更改代码时添加该变量并将颜色更改为像 pink 这样非常明显的颜色?此时,当你渲染页面时,你应该能够看到哪些东西被覆盖。

如果你正在制作 CSS 框架,我建议根据文档进行测试,因为这应该展示了之前可能会被覆盖的示例。


CSS Blocks

您可能还想了解CSS Blocks API。它不会是一个“测试”,但该API提供了作用域和编译错误,可以帮助您尽早发现其中的一些问题。

以下是相关部分:

使用CSS Blocks的新解析系统,层叠冲突将在您知道其存在之前就被捕获,您将永远不必再与优先级战斗。


0

CSS 优先级

根据文档,优先级是浏览器决定哪些 CSS 属性值对于一个元素最相关的逻辑,在存在两个或更多指向同一元素的冲突 CSS 规则时应用哪些规则。优先级是根据不同CSS 选择器组成的匹配规则计算得出的。


如何计算特异性

根据以下规则,可以基于点数计算特异性

  • style属性:1000
  • id属性:100
  • classpseudo-class:1

计算CSS特异性

让我们计算两个CSS的特异性。

  • 示例 A:

    .btn {
     color: red;
    }
    
    • 解释:包含一个 class,即 btn。因此,特异性为 1
  • 示例 B:

    .btn:disabled {
     color: green;
     ...
    }
    
    • 解释:包含一个 class,即 btn,以及一个 pseudo-class,即 disabled。因此,特异性为 2

测试

CSS 特异性也可以通过 特异性计算器 进行视觉验证:

Specificity


结论

由于CSS样例B具有更高的特异性,因此CSS样例B将应用于该元素:

<input class="btn" disabled id="test"></input>

结语

然而,在 CSS 的特殊性规则中还有一些更精细的规则:

  • 相等特殊性:最后的规则生效。
  • ID 选择器比属性选择器的特殊性更高。
  • 上下文选择器比单个元素选择器更具体。
  • 类选择器胜过任何数量的元素选择器。

您可以在CSS Specificity中找到详细的文档。


DebanjanB,感谢您的回答。也许从问题中并不清楚,但对我来说,主要问题是如何在单元测试中自动完成此操作。 - Torstein I. Bø
@TorsteinI.Bø 你不需要这样做。浏览器引擎根据权重来决定有效的CSS。我已经提到过_...CSS样本B具有更高的特异性,因此将应用CSS样本B到该元素..._ - undetected Selenium
1
但我想这样做。我正在制作一个大型框架,并希望使用测试驱动开发。这样可以在不破坏任何功能的情况下进行更改,然后我可以有测试来确保我拥有所需的样式。 - Torstein I. Bø
Selenium 无法帮助您在多个可用 CSS 中决定要应用哪个 CSS,但只能处理正在应用的 CSS。然而,Selenium 可以帮助您完全禁用 CSS。 - undetected Selenium

0

正如你所说,你可以通过Selenium实现这个功能。就方法论而言,如果你想长期维护这个功能,我建议使用Page Object模型。官方文档可以在这里找到,在其他语言中也有一些文章这里这里这里

本质上,这意味着为页面(或者是有多个控件的表单之类的页面部分/组件)创建类或者模型。这个类将会为每个需要与其交互的页面元素添加属性或字段。这种方法的优点包括:

  • 如果您需要更新选择器,则只需更改一个地方(可维护性)
  • 可以通过使用流畅的语法的漂亮界面来公开底层代码,即使它可能很丑陋(可读性)

这是一个示例(因为您没有指定语言,我将使用C#):

public class LoginPage
{
    // FindBy attributes should decorate each property to specify the selector
    public WebElement UsernameInput { get; set; }
    public WebElement PasswordInput { get; set; }
    public WebElement LoginButton { get; set; }

    public LoginPage()
    {
        ...
    }

    public LoginPage Load(this LoginPage page)
    {
        // code to navigate to the login page
    }

    public LoginPage EnterCredentials(this LoginPage page, string username, string password)
    {
        // code to fill out inputs
    }

    public HomePage Login(this LoginPage page)
    {
        // code to click login button
    }

    // Other methods
}

使用时它是这样的:

HomePage homePage =
    new LoginPage()
    .Load()
    .EnterCredentials("user", "pass")
    .Login();

// Now you can perform operations on the HomePage

2
不是下投票者,但我不明白这个答案与问题有任何关系。看起来像是楼主正在寻找类似于这样的东西:https://specificity.keegan.st - Ardesco

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接