#!/usr/bin/env ruby require File.dirname(__FILE__) + '/../test_helper' class SuperselectorTest < MiniTest::Test def test_superselector_reflexivity assert_superselector 'h1', 'h1' assert_superselector '.foo', '.foo' assert_superselector '#foo > .bar, baz', '#foo > .bar, baz' end def test_smaller_compound_superselector assert_strict_superselector '.foo', '.foo.bar' assert_strict_superselector '.bar', '.foo.bar' assert_strict_superselector 'a', 'a#b' assert_strict_superselector '#b', 'a#b' end def test_smaller_complex_superselector assert_strict_superselector '.bar', '.foo .bar' assert_strict_superselector '.bar', '.foo > .bar' assert_strict_superselector '.bar', '.foo + .bar' assert_strict_superselector '.bar', '.foo ~ .bar' end def test_selector_list_subset_superselector assert_strict_superselector '.foo, .bar', '.foo' assert_strict_superselector '.foo, .bar, .baz', '.foo, .baz' assert_strict_superselector '.foo, .baz, .qux', '.foo.bar, .baz.bang' end def test_leading_combinator_superselector refute_superselector '+ .foo', '.foo' refute_superselector '+ .foo', '.bar + .foo' end def test_trailing_combinator_superselector refute_superselector '.foo +', '.foo' refute_superselector '.foo +', '.foo + .bar' end def test_matching_combinator_superselector assert_strict_superselector '.foo + .bar', '.foo + .bar.baz' assert_strict_superselector '.foo + .bar', '.foo.baz + .bar' assert_strict_superselector '.foo > .bar', '.foo > .bar.baz' assert_strict_superselector '.foo > .bar', '.foo.baz > .bar' assert_strict_superselector '.foo ~ .bar', '.foo ~ .bar.baz' assert_strict_superselector '.foo ~ .bar', '.foo.baz ~ .bar' end def test_following_sibling_is_superselector_of_next_sibling assert_strict_superselector '.foo ~ .bar', '.foo + .bar.baz' assert_strict_superselector '.foo ~ .bar', '.foo.baz + .bar' end def test_descendant_is_superselector_of_child assert_strict_superselector '.foo .bar', '.foo > .bar.baz' assert_strict_superselector '.foo .bar', '.foo.baz > .bar' assert_strict_superselector '.foo .baz', '.foo > .bar > .baz' end def test_child_isnt_superselector_of_longer_child refute_superselector '.foo > .baz', '.foo > .bar > .baz' refute_superselector '.foo > .baz', '.foo > .bar .baz' end def test_following_sibling_isnt_superselector_of_longer_following_sibling refute_superselector '.foo + .baz', '.foo + .bar + .baz' refute_superselector '.foo + .baz', '.foo + .bar .baz' end def test_sibling_isnt_superselector_of_longer_sibling # This actually is a superselector, but it's a very narrow edge case and # detecting it is very difficult and may be exponential in the worst case. refute_superselector '.foo ~ .baz', '.foo ~ .bar ~ .baz' refute_superselector '.foo ~ .baz', '.foo ~ .bar .baz' end def test_matches_is_superselector_of_constituent_selectors %w[matches -moz-any].each do |name| assert_strict_superselector ":#{name}(.foo, .bar)", '.foo.baz' assert_strict_superselector ":#{name}(.foo, .bar)", '.bar.baz' assert_strict_superselector ":#{name}(.foo .bar, .baz)", '.x .foo .bar' end end def test_matches_is_superselector_of_subset_matches assert_strict_superselector ':matches(.foo, .bar, .baz)', '#x:matches(.foo.bip, .baz.bang)' assert_strict_superselector ':-moz-any(.foo, .bar, .baz)', '#x:-moz-any(.foo.bip, .baz.bang)' end def test_matches_is_not_superselector_of_any refute_superselector ':matches(.foo, .bar)', ':-moz-any(.foo, .bar)' refute_superselector ':-moz-any(.foo, .bar)', ':matches(.foo, .bar)' end def test_matches_can_be_subselector %w[matches -moz-any].each do |name| assert_superselector '.foo', ":#{name}(.foo.bar)" assert_superselector '.foo.bar', ":#{name}(.foo.bar.baz)" assert_superselector '.foo', ":#{name}(.foo.bar, .foo.baz)" end end def test_any_is_not_superselector_of_different_prefix refute_superselector ':-moz-any(.foo, .bar)', ':-s-any(.foo, .bar)' end def test_not_is_superselector_of_less_complex_not assert_strict_superselector ':not(.foo.bar)', ':not(.foo)' assert_strict_superselector ':not(.foo .bar)', ':not(.bar)' end def test_not_is_superselector_of_superset assert_strict_superselector ':not(.foo.bip, .baz.bang)', ':not(.foo, .bar, .baz)' assert_strict_superselector ':not(.foo.bip, .baz.bang)', ':not(.foo):not(.bar):not(.baz)' end def test_not_is_superselector_of_unique_selectors assert_strict_superselector ':not(h1.foo)', 'a' assert_strict_superselector ':not(.baz #foo)', '#bar' end def test_not_is_not_superselector_of_non_unique_selectors refute_superselector ':not(.foo)', '.bar' refute_superselector ':not(:hover)', ':visited' end def test_current_is_superselector_with_identical_innards assert_superselector ':current(.foo)', ':current(.foo)' end def test_current_is_superselector_with_subselector_innards refute_superselector ':current(.foo)', ':current(.foo.bar)' refute_superselector ':current(.foo.bar)', ':current(.foo)' end def test_nth_match_is_superselector_of_subset_nth_match assert_strict_superselector( ':nth-child(2n of .foo, .bar, .baz)', '#x:nth-child(2n of .foo.bip, .baz.bang)') assert_strict_superselector( ':nth-last-child(2n of .foo, .bar, .baz)', '#x:nth-last-child(2n of .foo.bip, .baz.bang)') end def test_nth_match_is_not_superselector_of_nth_match_with_different_arg refute_superselector( ':nth-child(2n of .foo, .bar, .baz)', '#x:nth-child(2n + 1 of .foo.bip, .baz.bang)') refute_superselector( ':nth-last-child(2n of .foo, .bar, .baz)', '#x:nth-last-child(2n + 1 of .foo.bip, .baz.bang)') end def test_nth_match_is_not_superselector_of_nth_last_match refute_superselector ':nth-child(2n of .foo, .bar)', ':nth-last-child(2n of .foo, .bar)' refute_superselector ':nth-last-child(2n of .foo, .bar)', ':nth-child(2n of .foo, .bar)' end def test_nth_match_can_be_subselector %w[nth-child nth-last-child].each do |name| assert_superselector '.foo', ":#{name}(2n of .foo.bar)" assert_superselector '.foo.bar', ":#{name}(2n of .foo.bar.baz)" assert_superselector '.foo', ":#{name}(2n of .foo.bar, .foo.baz)" end end def has_is_superselector_of_subset_host assert_strict_superselector ':has(.foo, .bar, .baz)', ':has(.foo.bip, .baz.bang)' end def has_isnt_superselector_of_contained_selector assert_strict_superselector ':has(.foo, .bar, .baz)', '.foo' end def host_is_superselector_of_subset_host assert_strict_superselector ':host(.foo, .bar, .baz)', ':host(.foo.bip, .baz.bang)' end def host_isnt_superselector_of_contained_selector assert_strict_superselector ':host(.foo, .bar, .baz)', '.foo' end def host_context_is_superselector_of_subset_host assert_strict_superselector( ':host-context(.foo, .bar, .baz)', ':host-context(.foo.bip, .baz.bang)') end def host_context_isnt_superselector_of_contained_selector assert_strict_superselector ':host-context(.foo, .bar, .baz)', '.foo' end private def assert_superselector(superselector, subselector) assert(parse_selector(superselector).superselector?(parse_selector(subselector)), "Expected #{superselector} to be a superselector of #{subselector}.") end def refute_superselector(superselector, subselector) assert(!parse_selector(superselector).superselector?(parse_selector(subselector)), "Expected #{superselector} not to be a superselector of #{subselector}.") end def assert_strict_superselector(superselector, subselector) assert_superselector(superselector, subselector) refute_superselector(subselector, superselector) end def parse_selector(selector) Sass::SCSS::CssParser.new(selector, filename_for_test, nil).parse_selector end end