test.js 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. import { describe, it } from 'mocha';
  2. import assert from 'assert';
  3. import { calculate, compare } from '../specificity';
  4. describe('calculate', () => {
  5. [
  6. // http://css-tricks.com/specifics-on-css-specificity/
  7. { selector: 'ul#nav li.active a', expected: '0,1,1,3' },
  8. { selector: 'body.ie7 .col_3 h2 ~ h2', expected: '0,0,2,3' },
  9. { selector: '#footer *:not(nav) li', expected: '0,1,0,2' },
  10. { selector: 'ul > li ul li ol li:first-letter', expected: '0,0,0,7' },
  11. // http://reference.sitepoint.com/css/specificity
  12. { selector: 'body#home div#warning p.message', expected: '0,2,1,3' },
  13. { selector: '* body#home>div#warning p.message', expected: '0,2,1,3' },
  14. { selector: '#home #warning p.message', expected: '0,2,1,1' },
  15. { selector: '#warning p.message', expected: '0,1,1,1' },
  16. { selector: '#warning p', expected: '0,1,0,1' },
  17. { selector: 'p.message', expected: '0,0,1,1' },
  18. { selector: 'p', expected: '0,0,0,1' },
  19. // Test pseudo-element with uppertestCase letters
  20. { selector: 'li:bEfoRE', expected: '0,0,0,2' },
  21. // Pseudo-class tests
  22. { selector: 'li:first-child+p', expected: '0,0,1,2'},
  23. { selector: 'li:nth-child(even)+p', expected: '0,0,1,2'},
  24. { selector: 'li:nth-child(2n+1)+p', expected: '0,0,1,2'},
  25. { selector: 'li:nth-child( 2n + 1 )+p', expected: '0,0,1,2'},
  26. { selector: 'li:nth-child(2n-1)+p', expected: '0,0,1,2'},
  27. { selector: 'li:nth-child(2n-1) p', expected: '0,0,1,2'},
  28. { selector: ':lang(nl-be)', expected: '0,0,1,0'},
  29. // Tests with CSS escape sequences
  30. // https://mathiasbynens.be/notes/css-escapes and https://mathiasbynens.be/demo/crazy-class
  31. { selector: '.\\3A -\\)', expected: '0,0,1,0' }, /* <p class=":-)"></p> */
  32. { selector: '.\\3A \\`\\(', expected: '0,0,1,0' }, /* <p class=":`("></p> */
  33. { selector: '.\\3A .\\`\\(', expected: '0,0,2,0' }, /* <p class=": `("></p> */
  34. { selector: '.\\31 a2b3c', expected: '0,0,1,0' }, /* <p class="1a2b3c"></p> */
  35. { selector: '.\\000031a2b3c', expected: '0,0,1,0' }, /* <p class="1a2b3c"></p> */
  36. { selector: '.\\000031 a2b3c', expected: '0,0,1,0' }, /* <p class="1a2b3c"></p> */
  37. { selector: '#\\#fake-id', expected: '0,1,0,0' }, /* <p id="#fake-id"></p> */
  38. { selector: '.\\#fake-id', expected: '0,0,1,0' }, /* <p class="#fake-id"></p> */
  39. { selector: '#\\<p\\>', expected: '0,1,0,0' }, /* <p id="<p>"></p> */
  40. { selector: '.\\#\\.\\#\\.\\#', expected: '0,0,1,0' }, /* <p class="#.#.#"></p> */
  41. { selector: '.foo\\.bar', expected: '0,0,1,0' }, /* <p class="foo.bar"></p> */
  42. { selector: '.\\:hover\\:active', expected: '0,0,1,0' }, /* <p class=":hover:active"></p> */
  43. { selector: '.\\3A hover\\3A active', expected: '0,0,1,0' }, /* <p class=":hover:active"></p> */
  44. { selector: '.\\000031 p', expected: '0,0,1,1' }, /* <p class="1"><p></p></p>" */
  45. { selector: '.\\3A \\`\\( .another', expected: '0,0,2,0' }, /* <p class=":`("><p class="another"></p></p> */
  46. { selector: '.\\--cool', expected: '0,0,1,0' }, /* <p class="--cool"></p> */
  47. { selector: '#home .\\[page\\]', expected: '0,1,1,0' }, /* <p id="home"><p class="[page]"></p></p> */
  48. // Test repeated IDs
  49. // https://github.com/keeganstreet/specificity/issues/29
  50. { selector: 'ul#nav#nav-main li.active a', expected: '0,2,1,3' },
  51. // Test CSS Modules https://github.com/css-modules/css-modules
  52. // Whilst they are not part of the CSS spec, this calculator can support them without breaking results for standard selectors
  53. { selector: '.root :global .text', expected: '0,0,2,0' },
  54. { selector: '.localA :global .global-b :local(.local-c) .global-d', expected: '0,0,4,0' },
  55. { selector: '.localA :global .global-b .global-c :local(.localD.localE) .global-d', expected: '0,0,6,0' },
  56. { selector: '.localA :global(.global-b) .local-b', expected: '0,0,3,0' },
  57. { selector: ':local(:nth-child(2n) .test)', expected: '0,0,2,0' },
  58. ].forEach(testCase => {
  59. it(`calculate("${testCase.selector}")`, () => {
  60. const result = calculate(testCase.selector);
  61. assert.equal(result[0].specificity, testCase.expected);
  62. });
  63. });
  64. });
  65. describe('compare', () => {
  66. [
  67. { a: 'div', b: 'span', expected: 0 },
  68. { a: '.active', b: ':focus', expected: 0 },
  69. { a: '#header', b: '#main', expected: 0 },
  70. { a: 'div', b: '.active', expected: -1 },
  71. { a: 'div', b: '#header', expected: -1 },
  72. { a: '.active', b: '#header', expected: -1 },
  73. { a: '.active', b: 'div', expected: 1 },
  74. { a: '#main', b: 'div', expected: 1 },
  75. { a: '#main', b: ':focus', expected: 1 },
  76. { a: 'div p', b: 'span a', expected: 0 },
  77. { a: '#main p .active', b: '#main span :focus', expected: 0 },
  78. { a: [0, 1, 1, 1], b: '#main span :focus', expected: 0 },
  79. { a: '#main p .active', b: [0, 1, 1, 1], expected: 0 },
  80. { a: ':focus', b: 'span a', expected: 1 },
  81. { a: '#main', b: 'span a:hover', expected: 1 },
  82. { a: 'ul > li > a > span:before', b: '.active', expected: -1 },
  83. { a: 'a.active:hover', b: '#main', expected: -1 },
  84. ].forEach(testCase => {
  85. it(`compare("${testCase.a}", "${testCase.b}")`, () => {
  86. const result = compare(testCase.a, testCase.b);
  87. assert.equal(result, testCase.expected);
  88. });
  89. });
  90. });
  91. describe('sorting with compare', () => {
  92. const a = 'div';
  93. const b = 'p a';
  94. const c = '.active';
  95. const d = 'p.active';
  96. const e = '.active:focus';
  97. const f = '#main';
  98. const original = [c, f, a, e, b, d];
  99. const sorted = [a, b, c, d, e, f];
  100. const result = original.sort(compare);
  101. it('Array.sort(compare) should sort the array by specificity', () => {
  102. assert.equal(result.join('|'), sorted.join('|'));
  103. });
  104. });