【问题标题】:CSS specificity testingCSS 特异性测试
【发布时间】:2019-11-03 01:50:51
【问题描述】:

有没有什么好的工具或方法可以自动测试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 值硬编码到测试中。

【问题讨论】:

  • “我想要一个测试,确保 id=test 的元素,将 .btn:disabled 作为“主要”css 类,将 .btn 作为“次要” .” - 所以你想检查那个 HTML 元素是否有这个类并且设置了disabled 属性?那将纯粹是在HTML上,resp。 DOM 级别 - 这与测试选择器是否正常工作有什么关系?你不是说你想要一个测试来检查这些规则的样式是否应用于元素吗?然后使用 getComputedStyle 进行检查。
  • “将 .btn:disabled 作为“主要”css 类,将 .btn 作为“次要”类。” - 没有“主要”和“二级 CSS 课程”。解释一下你的实际意思。
  • @TorsteinI.Bø 您能否为我们提供一些参考,说明 ..."primary" css 类......" 的确切含义次要"...
  • @04FS 我现在重新措辞,以便更清楚:-)
  • 您需要使用 CSSOM 来查找您的主要目标规则。将属性值与元素的计算样式进行比较。断言它们是相同的。将规则更改为属性的不同值并再次比较。如果它们仍然相同,则您的规则是主要规则。修改规则选择器,使其不匹配。从顶部重复您的次要目标规则,以及您要测试的每个进一步的目标规则。使用 Selenium 中的 JavascriptExecutor 应该可以实现所有这些。

标签: css unit-testing selenium testing css-selectors


【解决方案1】:

我解决的方法是使用 getComputedStyle 来获取“最高优先级”的样式。在 css 中,我向 content 属性添加了一个“标签”。然后在茉莉花中检查所需的标签是否是计算样式。 (我将在 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>

【讨论】:

    【解决方案2】:

    如果我正确理解了这个问题,那么您基本上是在要求进行这样的测试(以您的示例为例):

    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 块

    您可能还想查看CSS Blocks API。它不会完全是一个“测试”,但 API 提供了范围界定和编译错误,可能会帮助您尽早发现其中的一些问题。

    以下是相关部分:

    使用 CSS Blocks 新的解决系统,级联冲突会在你知道它们存在之前为你发现,你将永远不必再打一场特异性战争。

    【讨论】:

      【解决方案3】:

      CSS 特性

      根据文档Specificity 是浏览器决定哪些 CSS 属性值与元素最相关的逻辑,以防有两个或多个冲突的 CSS 规则指向同一个元素并且将应用哪些规则。根据不同CSS selectors组成的匹配规则计算特异性。


      如何计算特异性

      根据以下几点计算特异性有几个规则:

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

      Calculating the CSS Specificity

      让我们计算一下这两个 CSS 的Specificity

      • 样品 A:

        .btn {
         color: red;
        }
        
        • 说明:包含一个,即btn。所以特异性是1
      • 样品 B:

        .btn:disabled {
         color: green;
         ...
        }
        
        • 说明:包含一个,即btn和一个伪类,即disabled。所以特异性是2

      测试

      CSS特异性也可以通过Specificity Calculator进行视觉验证:


      结论

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

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

      结尾

      但是有一些更细化的 CSS 特殊性规则:

      • 同等特异性:最新规则也很重要。
      • ID 选择器比属性选择器具有更高的特异性。
      • 上下文选择器比单个元素选择器更具体。
      • 类选择器胜过任意数量的元素选择器。

      你可以在CSS Specificity找到详细的文档

      【讨论】:

      • DebanjanB 感谢您的回答。从问题中可能不清楚,但对我来说主要问题是如何在单元测试中自动执行此操作。
      • @TorsteinI.Bø 你不必这样做。浏览引擎根据权重决定有效的 CSS。我已经提到了...CSS sample B 具有更大的Specificity,所以CSS sample B 将应用于元素...
      • 但我想这样做。我正在制作一个大型框架,并希望使用测试驱动开发。这样可以在不破坏任何功能的情况下进行更改,然后我可以进行测试以确保我拥有所需的样式。
      • Selenium 无法帮助您决定在多个可用 CSS 中应用 CSS,但只能对正在应用的 CSS 起作用。但是,Selenium 可以帮助您完全禁用 CSS。
      【解决方案4】:

      正如您所提到的,您可以使用 Selenium 实现这一目标。就方法而言,如果您希望长期保持这种状态,那么我建议您遵循页面对象模型。这方面的官方文档在here,还有一些其他各种语言的文章hereherehere

      基本上它归结为为页面(或页面部分/组件(如在具有多个控件的表单中))创建类或模型,然后该类将具有每个元素的属性/字段您要与之交互的页面。这种方法的优点是:

      • 如果您需要更新选择器(可维护性),只需更改一个地方
      • 可以通过使用fluent syntax(可读性)的漂亮接口公开可能很难看的底层代码

      这看起来如何(因为你没有指定一种语言,我将使用 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
      

      【讨论】:

      • 不是反对者,但我不明白这个答案与这个问题有什么关系。听起来 OP 正在寻找这样的东西:specificity.keegan.st
      猜你喜欢
      • 2019-07-26
      • 2012-01-10
      • 1970-01-01
      • 2014-02-04
      • 2010-12-13
      • 1970-01-01
      • 1970-01-01
      • 2012-05-29
      • 2019-02-16
      相关资源
      最近更新 更多