feat(selector): add support for :not

Fixes #609
Closes #948
This commit is contained in:
Marc Laval
2015-03-12 09:44:49 +01:00
parent 5c1c534894
commit 8d2ee6bbda
2 changed files with 139 additions and 43 deletions

View File

@ -25,10 +25,10 @@ export function main() {
it('should select by element name case insensitive', () => {
matcher.addSelectable(s1 = CssSelector.parse('someTag'), 1);
matcher.match(CssSelector.parse('SOMEOTHERTAG'), selectableCollector);
expect(matcher.match(CssSelector.parse('SOMEOTHERTAG'), selectableCollector)).toEqual(false);
expect(matched).toEqual([]);
matcher.match(CssSelector.parse('SOMETAG'), selectableCollector);
expect(matcher.match(CssSelector.parse('SOMETAG'), selectableCollector)).toEqual(true);
expect(matched).toEqual([s1,1]);
});
@ -36,14 +36,14 @@ export function main() {
matcher.addSelectable(s1 = CssSelector.parse('.someClass'), 1);
matcher.addSelectable(s2 = CssSelector.parse('.someClass.class2'), 2);
matcher.match(CssSelector.parse('.SOMEOTHERCLASS'), selectableCollector);
expect(matcher.match(CssSelector.parse('.SOMEOTHERCLASS'), selectableCollector)).toEqual(false);
expect(matched).toEqual([]);
matcher.match(CssSelector.parse('.SOMECLASS'), selectableCollector);
expect(matcher.match(CssSelector.parse('.SOMECLASS'), selectableCollector)).toEqual(true);
expect(matched).toEqual([s1,1]);
reset();
matcher.match(CssSelector.parse('.someClass.class2'), selectableCollector);
expect(matcher.match(CssSelector.parse('.someClass.class2'), selectableCollector)).toEqual(true);
expect(matched).toEqual([s1,1,s2,2]);
});
@ -51,18 +51,18 @@ export function main() {
matcher.addSelectable(s1 = CssSelector.parse('[someAttr]'), 1);
matcher.addSelectable(s2 = CssSelector.parse('[someAttr][someAttr2]'), 2);
matcher.match(CssSelector.parse('[SOMEOTHERATTR]'), selectableCollector);
expect(matcher.match(CssSelector.parse('[SOMEOTHERATTR]'), selectableCollector)).toEqual(false);
expect(matched).toEqual([]);
matcher.match(CssSelector.parse('[SOMEATTR]'), selectableCollector);
expect(matcher.match(CssSelector.parse('[SOMEATTR]'), selectableCollector)).toEqual(true);
expect(matched).toEqual([s1,1]);
reset();
matcher.match(CssSelector.parse('[SOMEATTR=someValue]'), selectableCollector);
expect(matcher.match(CssSelector.parse('[SOMEATTR=someValue]'), selectableCollector)).toEqual(true);
expect(matched).toEqual([s1,1]);
reset();
matcher.match(CssSelector.parse('[someAttr][someAttr2]'), selectableCollector);
expect(matcher.match(CssSelector.parse('[someAttr][someAttr2]'), selectableCollector)).toEqual(true);
expect(matched).toEqual([s1,1,s2,2]);
});
@ -80,29 +80,29 @@ export function main() {
it('should select by attr name and value case insensitive', () => {
matcher.addSelectable(s1 = CssSelector.parse('[someAttr=someValue]'), 1);
matcher.match(CssSelector.parse('[SOMEATTR=SOMEOTHERATTR]'), selectableCollector);
expect(matcher.match(CssSelector.parse('[SOMEATTR=SOMEOTHERATTR]'), selectableCollector)).toEqual(false);
expect(matched).toEqual([]);
matcher.match(CssSelector.parse('[SOMEATTR=SOMEVALUE]'), selectableCollector);
expect(matcher.match(CssSelector.parse('[SOMEATTR=SOMEVALUE]'), selectableCollector)).toEqual(true);
expect(matched).toEqual([s1,1]);
});
it('should select by element name, class name and attribute name with value', () => {
matcher.addSelectable(s1 = CssSelector.parse('someTag.someClass[someAttr=someValue]'), 1);
matcher.match(CssSelector.parse('someOtherTag.someOtherClass[someOtherAttr]'), selectableCollector);
expect(matcher.match(CssSelector.parse('someOtherTag.someOtherClass[someOtherAttr]'), selectableCollector)).toEqual(false);
expect(matched).toEqual([]);
matcher.match(CssSelector.parse('someTag.someOtherClass[someOtherAttr]'), selectableCollector);
expect(matcher.match(CssSelector.parse('someTag.someOtherClass[someOtherAttr]'), selectableCollector)).toEqual(false);
expect(matched).toEqual([]);
matcher.match(CssSelector.parse('someTag.someClass[someOtherAttr]'), selectableCollector);
expect(matcher.match(CssSelector.parse('someTag.someClass[someOtherAttr]'), selectableCollector)).toEqual(false);
expect(matched).toEqual([]);
matcher.match(CssSelector.parse('someTag.someClass[someAttr]'), selectableCollector);
expect(matcher.match(CssSelector.parse('someTag.someClass[someAttr]'), selectableCollector)).toEqual(false);
expect(matched).toEqual([]);
matcher.match(CssSelector.parse('someTag.someClass[someAttr=someValue]'), selectableCollector);
expect(matcher.match(CssSelector.parse('someTag.someClass[someAttr=someValue]'), selectableCollector)).toEqual(true);
expect(matched).toEqual([s1,1]);
});
@ -112,21 +112,42 @@ export function main() {
matcher.addSelectable(s3 = CssSelector.parse('.class1.class2'), 3);
matcher.addSelectable(s4 = CssSelector.parse('.class2.class1'), 4);
matcher.match(CssSelector.parse('[someAttr].someClass'), selectableCollector);
expect(matcher.match(CssSelector.parse('[someAttr].someClass'), selectableCollector)).toEqual(true);
expect(matched).toEqual([s1,1,s2,2]);
reset();
matcher.match(CssSelector.parse('.someClass[someAttr]'), selectableCollector);
expect(matcher.match(CssSelector.parse('.someClass[someAttr]'), selectableCollector)).toEqual(true);
expect(matched).toEqual([s1,1,s2,2]);
reset();
matcher.match(CssSelector.parse('.class1.class2'), selectableCollector);
expect(matcher.match(CssSelector.parse('.class1.class2'), selectableCollector)).toEqual(true);
expect(matched).toEqual([s3,3,s4,4]);
reset();
matcher.match(CssSelector.parse('.class2.class1'), selectableCollector);
expect(matcher.match(CssSelector.parse('.class2.class1'), selectableCollector)).toEqual(true);
expect(matched).toEqual([s4,4,s3,3]);
});
it('should not select with a matching :not selector', () => {
matcher.addSelectable(CssSelector.parse('p:not(.someClass)'), 1);
matcher.addSelectable(CssSelector.parse('p:not([someAttr])'), 2);
matcher.addSelectable(CssSelector.parse(':not(.someClass)'), 3);
matcher.addSelectable(CssSelector.parse(':not(p)'), 4);
matcher.addSelectable(CssSelector.parse(':not(p[someAttr])'), 5);
expect(matcher.match(CssSelector.parse('p.someClass[someAttr]'), selectableCollector)).toEqual(false);
expect(matched).toEqual([]);
});
it('should select with a non matching :not selector', () => {
matcher.addSelectable(s1 = CssSelector.parse('p:not(.someClass)'), 1);
matcher.addSelectable(s2 = CssSelector.parse('p:not(.someOtherClass[someAttr])'), 2);
matcher.addSelectable(s3 = CssSelector.parse(':not(.someClass)'), 3);
matcher.addSelectable(s4 = CssSelector.parse(':not(.someOtherClass[someAttr])'), 4);
expect(matcher.match(CssSelector.parse('p[someOtherAttr].someOtherClass'), selectableCollector)).toEqual(true);
expect(matched).toEqual([s1,1,s2,2,s3,3,s4,4]);
});
});
describe('CssSelector.parse', () => {
@ -164,5 +185,36 @@ export function main() {
expect(cssSelector.toString()).toEqual('sometag.someclass[attrname=attrvalue]');
});
it('should detect :not', () => {
var cssSelector = CssSelector.parse('sometag:not([attrname=attrvalue].someclass)');
expect(cssSelector.element).toEqual('sometag');
expect(cssSelector.attrs.length).toEqual(0);
expect(cssSelector.classNames.length).toEqual(0);
var notSelector = cssSelector.notSelector;
expect(notSelector.element).toEqual(null);
expect(notSelector.attrs).toEqual(['attrname', 'attrvalue']);
expect(notSelector.classNames).toEqual(['someclass']);
expect(cssSelector.toString()).toEqual('sometag:not(.someclass[attrname=attrvalue])');
});
it('should detect :not without truthy', () => {
var cssSelector = CssSelector.parse(':not([attrname=attrvalue].someclass)');
expect(cssSelector.element).toEqual("*");
var notSelector = cssSelector.notSelector;
expect(notSelector.attrs).toEqual(['attrname', 'attrvalue']);
expect(notSelector.classNames).toEqual(['someclass']);
expect(cssSelector.toString()).toEqual('*:not(.someclass[attrname=attrvalue])');
});
it('should throw when nested :not', () => {
expect(() => {
CssSelector.parse('sometag:not(:not([attrname=attrvalue].someclass))')
}).toThrowError('Nesting :not is not allowed in a selector');
});
});
}