mirror of https://github.com/fb55/css-select.git
parent
7c3c405f04
commit
87afd4acd0
@ -0,0 +1,4 @@
|
||||
node_modules/
|
||||
coverage/
|
||||
lib/
|
||||
.nyc_output/
|
@ -0,0 +1,76 @@
|
||||
Some of the tests for this project are based on the NWMatcher, Qwery
|
||||
and Sizzle projects. License information below.
|
||||
|
||||
NWMatcher:
|
||||
|
||||
Copyright (c) 2007-2013 Diego Perini (http://www.iport.it)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
Qwery:
|
||||
|
||||
Copyright (C) 2012 Dustin Diaz & Jacob Thornton
|
||||
http://en.wikipedia.org/wiki/MIT_License
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
Sizzle:
|
||||
|
||||
Copyright JS Foundation and other contributors, https://js.foundation/
|
||||
|
||||
This software consists of voluntary contributions made by many
|
||||
individuals. For exact contribution history, see the revision history
|
||||
available at https://github.com/jquery/sizzle
|
||||
|
||||
The following license applies to all parts of this software except as
|
||||
documented below:
|
||||
|
||||
====
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
====
|
||||
|
||||
All files located in the node_modules and external directories are
|
||||
externally maintained libraries used by this software which have their
|
||||
own licenses; we recommend you read them, as their terms may differ from
|
||||
the terms above.
|
@ -1,11 +1,12 @@
|
||||
const CSSselect = require("../src");
|
||||
const makeDom = require("htmlparser2").parseDOM;
|
||||
const { falseFunc } = require("boolbase");
|
||||
const assert = require("assert");
|
||||
import * as CSSselect from "../src";
|
||||
import { parseDOM as makeDom } from "htmlparser2";
|
||||
import { falseFunc } from "boolbase";
|
||||
import * as assert from "assert";
|
||||
import type { Element } from "domhandler";
|
||||
|
||||
const dom = makeDom(
|
||||
'<div><div data-foo="In the end, it doesn\'t really matter."></div><div data-foo="Indeed-that\'s a delicate matter.">'
|
||||
);
|
||||
) as Element[];
|
||||
|
||||
describe("Attributes", () => {
|
||||
describe("ignore case", () => {
|
@ -1,30 +0,0 @@
|
||||
function isNode(v) {
|
||||
return !!(v && v.type);
|
||||
}
|
||||
function isNodeArray(v) {
|
||||
if (Array.isArray(v) && isNode(v[0])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
function NodeWithoutCircularReferences(node) {
|
||||
const newNode = {};
|
||||
Object.assign(newNode, node);
|
||||
delete newNode.next;
|
||||
delete newNode.prev;
|
||||
delete newNode.parent;
|
||||
if (isNodeArray(newNode.children)) {
|
||||
newNode.children = removeCircularRefs(newNode.children);
|
||||
}
|
||||
return newNode;
|
||||
}
|
||||
|
||||
function removeCircularRefs(value) {
|
||||
if (isNodeArray(value)) {
|
||||
return value.map(NodeWithoutCircularReferences);
|
||||
} else if (isNode(value)) {
|
||||
return NodeWithoutCircularReferences(value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
module.exports = removeCircularRefs;
|
@ -0,0 +1,37 @@
|
||||
import type { ElementType } from "htmlparser2";
|
||||
import type { Node, NodeWithChildren } from "domhandler";
|
||||
|
||||
interface NonCircularNode extends Omit<Partial<NodeWithChildren>, "children"> {
|
||||
type: ElementType.ElementType;
|
||||
children?: NonCircularNode[];
|
||||
}
|
||||
type AcceptedNode = NonCircularNode | Node;
|
||||
|
||||
function isNode(v?: AcceptedNode): boolean {
|
||||
return !!(v && v.type);
|
||||
}
|
||||
function isNodeArray(v?: AcceptedNode[]): v is Node[] {
|
||||
return Array.isArray(v) && isNode(v[0]);
|
||||
}
|
||||
|
||||
function NodeWithoutCircularReferences(node: Node): NonCircularNode {
|
||||
const newNode: NonCircularNode = { ...node };
|
||||
delete newNode.next;
|
||||
delete newNode.prev;
|
||||
delete newNode.parent;
|
||||
if (isNodeArray(newNode.children)) {
|
||||
newNode.children = newNode.children.map(NodeWithoutCircularReferences);
|
||||
}
|
||||
return newNode;
|
||||
}
|
||||
|
||||
export default function removeCircularRefs(
|
||||
value: Node[] | Node
|
||||
): NonCircularNode | NonCircularNode[] {
|
||||
if (Array.isArray(value) && isNodeArray(value)) {
|
||||
return value.map(NodeWithoutCircularReferences);
|
||||
} else if (isNode(value)) {
|
||||
return NodeWithoutCircularReferences(value);
|
||||
}
|
||||
return value;
|
||||
}
|
@ -1,10 +1,11 @@
|
||||
const CSSselect = require("../src");
|
||||
const makeDom = require("htmlparser2").parseDOM;
|
||||
const assert = require("assert");
|
||||
import * as CSSselect from "../src";
|
||||
import { parseDOM as makeDom } from "htmlparser2";
|
||||
import * as assert from "assert";
|
||||
import type { Element } from "domhandler";
|
||||
|
||||
const dom = makeDom(
|
||||
"<div><p>In the end, it doesn't really Matter.</p><div>Indeed-that's a delicate matter.</div>"
|
||||
);
|
||||
) as Element[];
|
||||
|
||||
describe("icontains", () => {
|
||||
describe("ignore case", () => {
|
@ -0,0 +1,770 @@
|
||||
/**
|
||||
* The NWMatcher Test Suite.
|
||||
* Adapted from https://github.com/dperini/nwmatcher/blob/master/test/scotch/test.js
|
||||
* (c) 2007-2013 Diego Perini (http://www.iport.it)
|
||||
*
|
||||
* See the LICENSE file for additional information.
|
||||
*/
|
||||
|
||||
import * as DomUtils from "domutils";
|
||||
import * as helper from "./tools/helper";
|
||||
import assert from "assert";
|
||||
import decircularize from "./decircularize";
|
||||
const document = helper.getDocument("nwmatcher.html");
|
||||
import * as CSSselect from "../src";
|
||||
import type { Node, Element } from "domhandler";
|
||||
|
||||
// Prototype's `$` function
|
||||
function getById(...args: string[]): Element | Element[] {
|
||||
if (args.some((arg) => typeof arg !== "string")) throw new Error();
|
||||
const elements = args.map((id) =>
|
||||
DomUtils.getElementById(id, document)
|
||||
) as Element[];
|
||||
return elements.length === 1 ? elements[0] : elements;
|
||||
}
|
||||
|
||||
// NWMatcher methods
|
||||
const select = (query: string, doc: Node[] | Node = document): Node[] =>
|
||||
CSSselect.selectAll(query, typeof doc === "string" ? select(doc) : doc);
|
||||
const match = CSSselect.is;
|
||||
|
||||
function assertEquivalent(
|
||||
v1: Node | Node[],
|
||||
v2: Node | Node[],
|
||||
message?: string
|
||||
) {
|
||||
return assert.deepStrictEqual(
|
||||
decircularize(v1),
|
||||
decircularize(v2),
|
||||
message
|
||||
);
|
||||
}
|
||||
|
||||
// The tests...
|
||||
describe("NWMatcher", () => {
|
||||
describe("Basic Selectors", () => {
|
||||
it("*", () => {
|
||||
// Universal selector
|
||||
const results = [];
|
||||
const nodes = document.getElementsByTagName("*");
|
||||
let index = 0;
|
||||
const { length } = nodes;
|
||||
let node;
|
||||
// Collect all element nodes, excluding comments (IE)
|
||||
for (; index < length; index++) {
|
||||
if ((node = nodes[index]).tagName !== "!") {
|
||||
results[results.length] = node;
|
||||
}
|
||||
}
|
||||
assertEquivalent(
|
||||
select("*"),
|
||||
results,
|
||||
"Comment nodes should be ignored."
|
||||
);
|
||||
});
|
||||
|
||||
it("E", () => {
|
||||
// Type selector
|
||||
const nodes = document.getElementsByTagName("li");
|
||||
assertEquivalent(select("li"), nodes);
|
||||
assert.strictEqual(
|
||||
select("strong", getById("fixtures"))[0],
|
||||
getById("strong")
|
||||
);
|
||||
assertEquivalent(select("nonexistent"), []);
|
||||
});
|
||||
|
||||
it("#id", () => {
|
||||
// ID selector
|
||||
assert.strictEqual(select("#fixtures")[0], getById("fixtures"));
|
||||
assertEquivalent(select("nonexistent"), []);
|
||||
assert.strictEqual(
|
||||
select("#troubleForm")[0],
|
||||
getById("troubleForm")
|
||||
);
|
||||
});
|
||||
|
||||
it(".class", () => {
|
||||
// Class selector
|
||||
assertEquivalent(
|
||||
select(".first"),
|
||||
getById("p", "link_1", "item_1")
|
||||
);
|
||||
assertEquivalent(select(".second"), []);
|
||||
});
|
||||
|
||||
it("E#id", () => {
|
||||
assert.strictEqual(select("strong#strong")[0], getById("strong"));
|
||||
assertEquivalent(select("p#strong"), []);
|
||||
});
|
||||
|
||||
it("E.class", () => {
|
||||
const secondLink = getById("link_2");
|
||||
assertEquivalent(select("a.internal"), getById("link_1", "link_2"));
|
||||
assert.strictEqual(select("a.internal.highlight")[0], secondLink);
|
||||
assert.strictEqual(select("a.highlight.internal")[0], secondLink);
|
||||
assertEquivalent(select("a.highlight.internal.nonexistent"), []);
|
||||
});
|
||||
|
||||
it("#id.class", () => {
|
||||
const secondLink = getById("link_2");
|
||||
assert.strictEqual(select("#link_2.internal")[0], secondLink);
|
||||
assert.strictEqual(select(".internal#link_2")[0], secondLink);
|
||||
assert.strictEqual(
|
||||
select("#link_2.internal.highlight")[0],
|
||||
secondLink
|
||||
);
|
||||
assertEquivalent(select("#link_2.internal.nonexistent"), []);
|
||||
});
|
||||
|
||||
it("E#id.class", () => {
|
||||
const secondLink = getById("link_2");
|
||||
assert.strictEqual(select("a#link_2.internal")[0], secondLink);
|
||||
assert.strictEqual(select("a.internal#link_2")[0], secondLink);
|
||||
assert.strictEqual(select("li#item_1.first")[0], getById("item_1"));
|
||||
assertEquivalent(select("li#item_1.nonexistent"), []);
|
||||
assertEquivalent(select("li#item_1.first.nonexistent"), []);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Attribute Selectors", () => {
|
||||
it("[foo]", () => {
|
||||
assertEquivalent(
|
||||
select("[href]", document.body),
|
||||
select("a[href]", document.body)
|
||||
);
|
||||
assertEquivalent(
|
||||
select("[class~=internal]"),
|
||||
select('a[class~="internal"]')
|
||||
);
|
||||
assertEquivalent(select("[id]"), select("*[id]"));
|
||||
assertEquivalent(
|
||||
select("[type=radio]"),
|
||||
getById("checked_radio", "unchecked_radio")
|
||||
);
|
||||
assertEquivalent(
|
||||
select("[type=checkbox]"),
|
||||
select("*[type=checkbox]")
|
||||
);
|
||||
assertEquivalent(
|
||||
select("[title]"),
|
||||
getById("with_title", "commaParent")
|
||||
);
|
||||
assertEquivalent(
|
||||
select("#troubleForm [type=radio]"),
|
||||
select("#troubleForm *[type=radio]")
|
||||
);
|
||||
assertEquivalent(
|
||||
select("#troubleForm [type]"),
|
||||
select("#troubleForm *[type]")
|
||||
);
|
||||
});
|
||||
|
||||
it("E[foo]", () => {
|
||||
assertEquivalent(
|
||||
select("h1[class]"),
|
||||
select("#fixtures h1"),
|
||||
"h1[class]"
|
||||
);
|
||||
assertEquivalent(
|
||||
select("h1[CLASS]"),
|
||||
select("#fixtures h1"),
|
||||
"h1[CLASS]"
|
||||
);
|
||||
assert.strictEqual(
|
||||
select("li#item_3[class]")[0],
|
||||
getById("item_3"),
|
||||
"li#item_3[class]"
|
||||
);
|
||||
assertEquivalent(
|
||||
select('#troubleForm2 input[name="brackets[5][]"]'),
|
||||
getById("chk_1", "chk_2")
|
||||
);
|
||||
// Brackets in attribute value
|
||||
assert.strictEqual(
|
||||
select('#troubleForm2 input[name="brackets[5][]"]:checked')[0],
|
||||
getById("chk_1")
|
||||
);
|
||||
// Space in attribute value
|
||||
assert.strictEqual(
|
||||
select('cite[title="hello world!"]')[0],
|
||||
getById("with_title")
|
||||
);
|
||||
// Namespaced attributes
|
||||
// assertEquivalent(select('[xml:lang]'), [document.documentElement, getById("item_3")]);
|
||||
// assertEquivalent(select('*[xml:lang]'), [document.documentElement, getById("item_3")]);
|
||||
});
|
||||
|
||||
it('E[foo="bar"]', () => {
|
||||
assertEquivalent(
|
||||
select('a[href="#"]'),
|
||||
getById("link_1", "link_2", "link_3")
|
||||
);
|
||||
// assert.throws(() => select("a[href=#]"));
|
||||
assert.strictEqual(
|
||||
select(
|
||||
'#troubleForm2 input[name="brackets[5][]"][value="2"]'
|
||||
)[0],
|
||||
getById("chk_2")
|
||||
);
|
||||
});
|
||||
|
||||
it('E[foo~="bar"]', () => {
|
||||
assertEquivalent(
|
||||
select('a[class~="internal"]'),
|
||||
getById("link_1", "link_2"),
|
||||
'a[class~="internal"]'
|
||||
);
|
||||
assertEquivalent(
|
||||
select("a[class~=internal]"),
|
||||
getById("link_1", "link_2"),
|
||||
"a[class~=internal]"
|
||||
);
|
||||
assert.strictEqual(
|
||||
select('a[class~=external][href="#"]')[0],
|
||||
getById("link_3"),
|
||||
'a[class~=external][href="#"]'
|
||||
);
|
||||
});
|
||||
|
||||
it.skip('E[foo|="en"]', () => {
|
||||
assert.strictEqual(
|
||||
select('*[xml:lang|="es"]')[0],
|
||||
getById("item_3")
|
||||
);
|
||||
assert.strictEqual(
|
||||
select('*[xml:lang|="ES"]')[0],
|
||||
getById("item_3")
|
||||
);
|
||||
});
|
||||
|
||||
it('E[foo^="bar"]', () => {
|
||||
assertEquivalent(
|
||||
select("div[class^=bro]"),
|
||||
getById("father", "uncle"),
|
||||
"matching beginning of string"
|
||||
);
|
||||
assertEquivalent(
|
||||
select('#level1 *[id^="level2_"]'),
|
||||
getById("level2_1", "level2_2", "level2_3")
|
||||
);
|
||||
assertEquivalent(
|
||||
select("#level1 *[id^=level2_]"),
|
||||
getById("level2_1", "level2_2", "level2_3")
|
||||
);
|
||||
});
|
||||
|
||||
it('E[foo$="bar"]', () => {
|
||||
assertEquivalent(
|
||||
select("div[class$=men]"),
|
||||
getById("father", "uncle"),
|
||||
"matching end of string"
|
||||
);
|
||||
assertEquivalent(
|
||||
select('#level1 *[id$="_1"]'),
|
||||
getById("level2_1", "level3_1")
|
||||
);
|
||||
assertEquivalent(
|
||||
select("#level1 *[id$=_1]"),
|
||||
getById("level2_1", "level3_1")
|
||||
);
|
||||
});
|
||||
|
||||
it('E[foo*="bar"]', () => {
|
||||
assertEquivalent(
|
||||
select('div[class*="ers m"]'),
|
||||
getById("father", "uncle"),
|
||||
"matching substring"
|
||||
);
|
||||
assertEquivalent(
|
||||
select('#level1 *[id*="2"]'),
|
||||
getById("level2_1", "level3_2", "level2_2", "level2_3")
|
||||
);
|
||||
// assert.throws(() => select("#level1 *[id*=2]"));
|
||||
});
|
||||
});
|
||||
|
||||
describe("Structural pseudo-classes", () => {
|
||||
it("E:first-child", () => {
|
||||
assert.strictEqual(
|
||||
select("#level1>*:first-child")[0],
|
||||
getById("level2_1")
|
||||
);
|
||||
assertEquivalent(
|
||||
select("#level1 *:first-child"),
|
||||
getById("level2_1", "level3_1", "level_only_child")
|
||||
);
|
||||
assertEquivalent(select("#level1>div:first-child"), []);
|
||||
assertEquivalent(
|
||||
select("#level1 span:first-child"),
|
||||
getById("level2_1", "level3_1")
|
||||
);
|
||||
assertEquivalent(select("#level1:first-child"), []);
|
||||
});
|
||||
|
||||
it("E:last-child", () => {
|
||||
assert.strictEqual(
|
||||
select("#level1>*:last-child")[0],
|
||||
getById("level2_3")
|
||||
);
|
||||
assertEquivalent(
|
||||
select("#level1 *:last-child"),
|
||||
getById("level3_2", "level_only_child", "level2_3")
|
||||
);
|
||||
assert.strictEqual(
|
||||
select("#level1>div:last-child")[0],
|
||||
getById("level2_3")
|
||||
);
|
||||
assert.strictEqual(
|
||||
select("#level1 div:last-child")[0],
|
||||
getById("level2_3")
|
||||
);
|
||||
assertEquivalent(select("#level1>span:last-child"), []);
|
||||
});
|
||||
|
||||
it("E:nth-child(n)", () => {
|
||||
assert.strictEqual(
|
||||
select("#p *:nth-child(3)")[0],
|
||||
getById("link_2")
|
||||
);
|
||||
assert.strictEqual(
|
||||
select("#p a:nth-child(3)")[0],
|
||||
getById("link_2"),
|
||||
"nth-child"
|
||||
);
|
||||
assertEquivalent(
|
||||
select("#list > li:nth-child(n+2)"),
|
||||
getById("item_2", "item_3")
|
||||
);
|
||||
assertEquivalent(
|
||||
select("#list > li:nth-child(-n+2)"),
|
||||
getById("item_1", "item_2")
|
||||
);
|
||||
});
|
||||
|
||||
it("E:nth-of-type(n)", () => {
|
||||
assert.strictEqual(
|
||||
select("#p a:nth-of-type(2)")[0],
|
||||
getById("link_2"),
|
||||
"nth-of-type"
|
||||
);
|
||||
assert.strictEqual(
|
||||
select("#p a:nth-of-type(1)")[0],
|
||||
getById("link_1"),
|
||||
"nth-of-type"
|
||||
);
|
||||
});
|
||||
|
||||
it("E:nth-last-of-type(n)", () => {
|
||||
assert.strictEqual(
|
||||
select("#p a:nth-last-of-type(1)")[0],
|
||||
getById("link_2"),
|
||||
"nth-last-of-type"
|
||||
);
|
||||
});
|
||||
|
||||
it("E:first-of-type", () => {
|
||||
assert.strictEqual(
|
||||
select("#p a:first-of-type")[0],
|
||||
getById("link_1"),
|
||||
"first-of-type"
|
||||
);
|
||||
});
|
||||
|
||||
it("E:last-of-type", () => {
|
||||
assert.strictEqual(
|
||||
select("#p a:last-of-type")[0],
|
||||
getById("link_2"),
|
||||
"last-of-type"
|
||||
);
|
||||
});
|
||||
|
||||
it("E:only-child", () => {
|
||||
assert.strictEqual(
|
||||
select("#level1 *:only-child")[0],
|
||||
getById("level_only_child")
|
||||
);
|
||||
// Shouldn't return anything
|
||||
assertEquivalent(select("#level1>*:only-child"), []);
|
||||
assertEquivalent(select("#level1:only-child"), []);
|
||||
assertEquivalent(
|
||||
select("#level2_2 :only-child:not(:last-child)"),
|
||||
[]
|
||||
);
|
||||
assertEquivalent(
|
||||
select("#level2_2 :only-child:not(:first-child)"),
|
||||
[]
|
||||
);
|
||||
});
|
||||
|
||||
it("E:empty", () => {
|
||||
(getById("level3_1") as Element).children = [];
|
||||
|
||||
assert.strictEqual(
|
||||
select("#level3_1:empty")[0],
|
||||
getById("level3_1"),
|
||||
"IE forced empty content!"
|
||||
);
|
||||
|
||||
// Shouldn't return anything
|
||||
assertEquivalent(select("span:empty > *"), []);
|
||||
});
|
||||
});
|
||||
|
||||
describe(":not", () => {
|
||||
it("E:not(s)", () => {
|
||||
// Negation pseudo-class
|
||||
assertEquivalent(select('a:not([href="#"])'), []);
|
||||
assertEquivalent(select("div.brothers:not(.brothers)"), []);
|
||||
assertEquivalent(
|
||||
select('a[class~=external]:not([href="#"])'),
|
||||
[],
|
||||
'a[class~=external][href!="#"]'
|
||||
);
|
||||
assert.strictEqual(
|
||||
select("#p a:not(:first-of-type)")[0],
|
||||
getById("link_2"),
|
||||
"first-of-type"
|
||||
);
|
||||
assert.strictEqual(
|
||||
select("#p a:not(:last-of-type)")[0],
|
||||
getById("link_1"),
|
||||
"last-of-type"
|
||||
);
|
||||
assert.strictEqual(
|
||||
select("#p a:not(:nth-of-type(1))")[0],
|
||||
getById("link_2"),
|
||||
"nth-of-type"
|
||||
);
|
||||
assert.strictEqual(
|
||||
select("#p a:not(:nth-last-of-type(1))")[0],
|
||||
getById("link_1"),
|
||||
"nth-last-of-type"
|
||||
);
|
||||
assert.strictEqual(
|
||||
select("#p a:not([rel~=nofollow])")[0],
|
||||
getById("link_2"),
|
||||
"attribute 1"
|
||||
);
|
||||
assert.strictEqual(
|
||||
select("#p a:not([rel^=external])")[0],
|
||||
getById("link_2"),
|
||||
"attribute 2"
|
||||
);
|
||||
assert.strictEqual(
|
||||
select("#p a:not([rel$=nofollow])")[0],
|
||||
getById("link_2"),
|
||||
"attribute 3"
|
||||
);
|
||||
assert.strictEqual(
|
||||
select('#p a:not([rel$="nofollow"]) > em')[0],
|
||||
getById("em"),
|
||||
"attribute 4"
|
||||
);
|
||||
assert.strictEqual(
|
||||
select("#list li:not(#item_1):not(#item_3)")[0],
|
||||
getById("item_2"),
|
||||
"adjacent :not clauses"
|
||||
);
|
||||
assert.strictEqual(
|
||||
select("#grandfather > div:not(#uncle) #son")[0],
|
||||
getById("son")
|
||||
);
|
||||
assert.strictEqual(
|
||||
select('#p a:not([rel$="nofollow"]) em')[0],
|
||||
getById("em"),
|
||||
"attribute 4 + all descendants"
|
||||
);
|
||||
assert.strictEqual(
|
||||
select('#p a:not([rel$="nofollow"])>em')[0],
|
||||
getById("em"),
|
||||
"attribute 4 (without whitespace)"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("UI element states pseudo-classes", () => {
|
||||
it("E:disabled", () => {
|
||||
assert.strictEqual(
|
||||
select("#troubleForm > p > *:disabled")[0],
|
||||
getById("disabled_text_field")
|
||||
);
|
||||
});
|
||||
|
||||
it("E:checked", () => {
|
||||
assertEquivalent(
|
||||
select("#troubleForm *:checked"),
|
||||
getById("checked_box", "checked_radio")
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Combinators", () => {
|
||||
it("E F", () => {
|
||||
// Descendant
|
||||
assertEquivalent(
|
||||
select("#fixtures a *"),
|
||||
getById("em2", "em", "span")
|
||||
);
|
||||
assert.strictEqual(select("div#fixtures p")[0], getById("p"));
|
||||
});
|
||||
|
||||
it("E + F", () => {
|
||||
// Adjacent sibling
|
||||
assert.strictEqual(
|
||||
select("div.brothers + div.brothers")[0],
|
||||
getById("uncle")
|
||||
);
|
||||
assert.strictEqual(
|
||||
select("div.brothers + div")[0],
|
||||
getById("uncle")
|
||||
);
|
||||
assert.strictEqual(
|
||||
select("#level2_1+span")[0],
|
||||
getById("level2_2")
|
||||
);
|
||||
assert.strictEqual(
|
||||
select("#level2_1 + span")[0],
|
||||
getById("level2_2")
|
||||
);
|
||||
assert.strictEqual(select("#level2_1 + *")[0], getById("level2_2"));
|
||||
assertEquivalent(select("#level2_2 + span"), []);
|
||||
assert.strictEqual(
|
||||
select("#level3_1 + span")[0],
|
||||
getById("level3_2")
|
||||
);
|
||||
assert.strictEqual(select("#level3_1 + *")[0], getById("level3_2"));
|
||||
assertEquivalent(select("#level3_2 + *"), []);
|
||||
assertEquivalent(select("#level3_1 + em"), []);
|
||||
|
||||
assert.strictEqual(
|
||||
select("+ div.brothers", select("div.brothers"))[0],
|
||||
getById("uncle")
|
||||
);
|
||||
assert.strictEqual(
|
||||
select("+ div", select("div.brothers"))[0],
|
||||
getById("uncle")
|
||||
);
|
||||
assert.strictEqual(
|
||||
select("+span", select("#level2_1"))[0],
|
||||
getById("level2_2")
|
||||
);
|
||||
assert.strictEqual(
|
||||
select("+ span", select("#level2_1"))[0],
|
||||
getById("level2_2")
|
||||
);
|
||||
assert.strictEqual(
|
||||
select("+ *", select("#level2_1"))[0],
|
||||
getById("level2_2")
|
||||
);
|
||||
assertEquivalent(select("+ span", select("#level2_2")), []);
|
||||
assert.strictEqual(
|
||||
select("+ span", select("#level3_1"))[0],
|
||||
getById("level3_2")
|
||||
);
|
||||
assert.strictEqual(
|
||||
select("+ *", select("#level3_1"))[0],
|
||||
getById("level3_2")
|
||||
);
|
||||
assertEquivalent(select("+ *", select("#level3_2")), []);
|
||||
assertEquivalent(select("+ em", select("#level3_1")), []);
|
||||
});
|
||||
|
||||
it("E > F", () => {
|
||||
// Child
|
||||
assertEquivalent(
|
||||
select("p.first > a"),
|
||||
getById("link_1", "link_2")
|
||||
);
|
||||
assertEquivalent(
|
||||
select("div#grandfather > div"),
|
||||
getById("father", "uncle")
|
||||
);
|
||||
assertEquivalent(
|
||||
select("#level1>span"),
|
||||
getById("level2_1", "level2_2")
|
||||
);
|
||||
assertEquivalent(
|
||||
select("#level1 > span"),
|
||||
getById("level2_1", "level2_2")
|
||||
);
|
||||
assertEquivalent(
|
||||
select("#level2_1 > *"),
|
||||
getById("level3_1", "level3_2")
|
||||
);
|
||||
assertEquivalent(select("div > #nonexistent"), []);
|
||||
|
||||
assertEquivalent(
|
||||
select("> a", select("p.first")),
|
||||
getById("link_1", "link_2")
|
||||
);
|
||||
assertEquivalent(
|
||||
select("> div", select("div#grandfather")),
|
||||
getById("father", "uncle")
|
||||
);
|
||||
assertEquivalent(
|
||||
select(">span", select("#level1")),
|
||||
getById("level2_1", "level2_2")
|
||||
);
|
||||
assertEquivalent(
|
||||
select("> span", select("#level1")),
|
||||
getById("level2_1", "level2_2")
|
||||
);
|
||||
assertEquivalent(
|
||||
select("> *", select("#level2_1")),
|
||||
getById("level3_1", "level3_2")
|
||||
);
|
||||
assertEquivalent(select("> #nonexistent", select("div")), []);
|
||||
});
|
||||
|
||||
it("E ~ F", () => {
|
||||
// General sibling
|
||||
assert.strictEqual(select("h1 ~ ul")[0], getById("list"));
|
||||
assertEquivalent(select("#level2_2 ~ span"), []);
|
||||
assertEquivalent(select("#level3_2 ~ *"), []);
|
||||
assertEquivalent(select("#level3_1 ~ em"), []);
|
||||
assertEquivalent(select("div ~ #level3_2"), []);
|
||||
assertEquivalent(select("div ~ #level2_3"), []);
|
||||
assert.strictEqual(
|
||||
select("#level2_1 ~ span")[0],
|
||||
getById("level2_2")
|
||||
);
|
||||
assertEquivalent(
|
||||
select("#level2_1 ~ *"),
|
||||
getById("level2_2", "level2_3")
|
||||
);
|
||||
assert.strictEqual(
|
||||
select("#level3_1 ~ #level3_2")[0],
|
||||
getById("level3_2")
|
||||
);
|
||||
assert.strictEqual(
|
||||
select("span ~ #level3_2")[0],
|
||||
getById("level3_2")
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
select("~ ul", select("h1"))[0],
|
||||
getById("list")
|
||||
);
|
||||
assertEquivalent(select("~ span", select("#level2_2")), []);
|
||||
assertEquivalent(select("~ *", select("#level3_2")), []);
|
||||
assertEquivalent(select("~ em", select("#level3_1")), []);
|
||||
assertEquivalent(select("~ #level3_2", select("div")), []);
|
||||
assertEquivalent(select("~ #level2_3", select("div")), []);
|
||||
assert.strictEqual(
|
||||
select("~ span", select("#level2_1"))[0],
|
||||
getById("level2_2")
|
||||
);
|
||||
assertEquivalent(
|
||||
select("~ *", select("#level2_1")),
|
||||
getById("level2_2", "level2_3")
|
||||
);
|
||||
assert.strictEqual(
|
||||
select("~ #level3_2", select("#level3_1"))[0],
|
||||
getById("level3_2")
|
||||
);
|
||||
assert.strictEqual(
|
||||
select("~ #level3_2", select("span"))[0],
|
||||
getById("level3_2")
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Diverse", () => {
|
||||
it("NW.Dom.match", () => {
|
||||
const element = getById("dupL1");
|
||||
// Assertions
|
||||
assert.ok(match(element, "span"));
|
||||
assert.ok(match(element, "span#dupL1"));
|
||||
assert.ok(match(element, "div > span"), "child combinator");
|
||||
assert.ok(
|
||||
match(element, "#dupContainer span"),
|
||||
"descendant combinator"
|
||||
);
|
||||
assert.ok(match(element, "#dupL1"), "ID only");
|
||||
assert.ok(match(element, "span.span_foo"), "class name 1");
|
||||
assert.ok(match(element, "span.span_bar"), "class name 2");
|
||||
assert.ok(
|
||||
match(element, "span:first-child"),
|
||||
"first-child pseudoclass"
|
||||
);
|
||||
// Refutations
|
||||
assert.ok(!match(element, "span.span_wtf"), "bogus class name");
|
||||
assert.ok(!match(element, "#dupL2"), "different ID");
|
||||
assert.ok(!match(element, "div"), "different tag name");
|
||||
assert.ok(!match(element, "span span"), "different ancestry");
|
||||
assert.ok(!match(element, "span > span"), "different parent");
|
||||
assert.ok(
|
||||
!match(element, "span:nth-child(5)"),
|
||||
"different pseudoclass"
|
||||
);
|
||||
// Misc.
|
||||
assert.ok(!match(getById("link_2"), "a[rel^=external]"));
|
||||
assert.ok(match(getById("link_1"), "a[rel^=external]"));
|
||||
assert.ok(match(getById("link_1"), 'a[rel^="external"]'));
|
||||
assert.ok(match(getById("link_1"), "a[rel^='external']"));
|
||||
});
|
||||
|
||||
it("Equivalent Selectors", () => {
|
||||
assertEquivalent(
|
||||
select("div.brothers"),
|
||||
select("div[class~=brothers]")
|
||||
);
|
||||
assertEquivalent(
|
||||
select("div.brothers"),
|
||||
select("div[class~=brothers].brothers")
|
||||
);
|
||||
assertEquivalent(
|
||||
select("div:not(.brothers)"),
|
||||
select("div:not([class~=brothers])")
|
||||
);
|
||||
assertEquivalent(select("li ~ li"), select("li:not(:first-child)"));
|
||||
assertEquivalent(select("ul > li"), select("ul > li:nth-child(n)"));
|
||||
assertEquivalent(
|
||||
select("ul > li:nth-child(even)"),
|
||||
select("ul > li:nth-child(2n)")
|
||||
);
|
||||
assertEquivalent(
|
||||
select("ul > li:nth-child(odd)"),
|
||||
select("ul > li:nth-child(2n+1)")
|
||||
);
|
||||
assertEquivalent(
|
||||
select("ul > li:first-child"),
|
||||
select("ul > li:nth-child(1)")
|
||||
);
|
||||
assertEquivalent(
|
||||
select("ul > li:last-child"),
|
||||
select("ul > li:nth-last-child(1)")
|
||||
);
|
||||
/* Opera 10 does not accept values > 128 as a parameter to :nth-child
|
||||
See <http://operawiki.info/ArtificialLimits> */
|
||||
assertEquivalent(
|
||||
select("ul > li:nth-child(n-128)"),
|
||||
select("ul > li")
|
||||
);
|
||||
assertEquivalent(select("ul>li"), select("ul > li"));
|
||||
assertEquivalent(
|
||||
select('#p a:not([rel$="nofollow"])>em'),
|
||||
select('#p a:not([rel$="nofollow"]) > em')
|
||||
);
|
||||
});
|
||||
|
||||
it("Multiple Selectors", () => {
|
||||
// The next two assertions should return document-ordered lists of matching elements --Diego Perini
|
||||
// assertEquivalent(select('#list, .first,*[xml:lang="es-us"] , #troubleForm'), getById('p', 'link_1', 'list', 'item_1', 'item_3', 'troubleForm'));
|
||||
// assertEquivalent(select('#list, .first, *[xml:lang="es-us"], #troubleForm'), getById('p', 'link_1', 'list', 'item_1', 'item_3', 'troubleForm'));
|
||||
assertEquivalent(
|
||||
select(
|
||||
'form[title*="commas,"], input[value="#commaOne,#commaTwo"]'
|
||||
),
|
||||
getById("commaParent", "commaChild")
|
||||
);
|
||||
assertEquivalent(
|
||||
select(
|
||||
'form[title*="commas,"], input[value="#commaOne,#commaTwo"]'
|
||||
),
|
||||
getById("commaParent", "commaChild")
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
@ -1,22 +0,0 @@
|
||||
Copyright (c) 2007-2013 Diego Perini (http://www.iport.it)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
@ -1,874 +0,0 @@
|
||||
/*
|
||||
taken from https://github.com/dperini/nwmatcher/blob/master/test/scotch/test.js
|
||||
*/
|
||||
|
||||
const { DomUtils } = require("htmlparser2");
|
||||
const helper = require("../tools/helper.js");
|
||||
const assert = require("assert");
|
||||
const path = require("path");
|
||||
const decircularize = require("../decircularize");
|
||||
const document = helper.getDocument(path.join(__dirname, "test.html"));
|
||||
const CSSselect = require("../../src");
|
||||
|
||||
// Prototype's `$` function
|
||||
function getById(...args) {
|
||||
if (args.some((arg) => typeof arg !== "string")) throw new Error();
|
||||
const elements = args.map((id) => DomUtils.getElementById(id, document));
|
||||
return elements.length === 1 ? elements[0] : elements;
|
||||
}
|
||||
|
||||
// NWMatcher methods
|
||||
const select = (query, doc = document) =>
|
||||
CSSselect.selectAll(query, typeof doc === "string" ? select(doc) : doc);
|
||||
const match = CSSselect.is;
|
||||
|
||||
const validators = {
|
||||
assert,
|
||||
assertEqual: assert.strictEqual,
|
||||
assertEquivalent(v1, v2, message) {
|
||||
return assert.deepStrictEqual(
|
||||
decircularize(v1),
|
||||
decircularize(v2),
|
||||
message
|
||||
);
|
||||
},
|
||||
refute: function refute(a, msg) {
|
||||
assert(!a, msg);
|
||||
},
|
||||
assertThrowsException() {}, // not implemented
|
||||
};
|
||||
|
||||
const runner = {
|
||||
__name: "",
|
||||
addGroup(name) {
|
||||
this.__name = name;
|
||||
return this;
|
||||
},
|
||||
addTests(_, tests) {
|
||||
if (this.__name) {
|
||||
describe(this.__name, run);
|
||||
this.__name = "";
|
||||
} else run();
|
||||
|
||||
function run() {
|
||||
for (const name of Object.keys(tests)) {
|
||||
it(name, () => {
|
||||
tests[name].call(validators);
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const RUN_BENCHMARKS = false;
|
||||
// The tests...
|
||||
(function (runner) {
|
||||
runner.addGroup("Basic Selectors").addTests(null, {
|
||||
"*"() {
|
||||
// Universal selector
|
||||
const results = [];
|
||||
const nodes = document.getElementsByTagName("*");
|
||||
let index = 0;
|
||||
const { length } = nodes;
|
||||
let node;
|
||||
// Collect all element nodes, excluding comments (IE)
|
||||
for (; index < length; index++) {
|
||||
if ((node = nodes[index]).tagName !== "!") {
|
||||
results[results.length] = node;
|
||||
}
|
||||
}
|
||||
this.assertEquivalent(
|
||||
select("*"),
|
||||
results,
|
||||
"Comment nodes should be ignored."
|
||||
);
|
||||
},
|
||||
E() {
|
||||
// Type selector
|
||||
const nodes = document.getElementsByTagName("li");
|
||||
this.assertEquivalent(select("li"), nodes);
|
||||
this.assertEqual(
|
||||
select("strong", getById("fixtures"))[0],
|
||||
getById("strong")
|
||||
);
|
||||
this.assertEquivalent(select("nonexistent"), []);
|
||||
},
|
||||
"#id"() {
|
||||
// ID selector
|
||||
this.assertEqual(select("#fixtures")[0], getById("fixtures"));
|
||||
this.assertEquivalent(select("nonexistent"), []);
|
||||
this.assertEqual(select("#troubleForm")[0], getById("troubleForm"));
|
||||
},
|
||||
".class"() {
|
||||
// Class selector
|
||||
this.assertEquivalent(
|
||||
select(".first"),
|
||||
getById("p", "link_1", "item_1")
|
||||
);
|
||||
this.assertEquivalent(select(".second"), []);
|
||||
},
|
||||
"E#id"() {
|
||||
this.assertEqual(select("strong#strong")[0], getById("strong"));
|
||||
this.assertEquivalent(select("p#strong"), []);
|
||||
},
|
||||
"E.class"() {
|
||||
const secondLink = getById("link_2");
|
||||
this.assertEquivalent(
|
||||
select("a.internal"),
|
||||
getById("link_1", "link_2")
|
||||
);
|
||||
this.assertEqual(select("a.internal.highlight")[0], secondLink);
|
||||
this.assertEqual(select("a.highlight.internal")[0], secondLink);
|
||||
this.assertEquivalent(
|
||||
select("a.highlight.internal.nonexistent"),
|
||||
[]
|
||||
);
|
||||
},
|
||||
"#id.class"() {
|
||||
const secondLink = getById("link_2");
|
||||
this.assertEqual(select("#link_2.internal")[0], secondLink);
|
||||
this.assertEqual(select(".internal#link_2")[0], secondLink);
|
||||
this.assertEqual(
|
||||
select("#link_2.internal.highlight")[0],
|
||||
secondLink
|
||||
);
|
||||
this.assertEquivalent(select("#link_2.internal.nonexistent"), []);
|
||||
},
|
||||
"E#id.class"() {
|
||||
const secondLink = getById("link_2");
|
||||
this.assertEqual(select("a#link_2.internal")[0], secondLink);
|
||||
this.assertEqual(select("a.internal#link_2")[0], secondLink);
|
||||
this.assertEqual(select("li#item_1.first")[0], getById("item_1"));
|
||||
this.assertEquivalent(select("li#item_1.nonexistent"), []);
|
||||
this.assertEquivalent(select("li#item_1.first.nonexistent"), []);
|
||||
},
|
||||
});
|
||||
|
||||
runner.addGroup("Attribute Selectors").addTests(null, {
|
||||
"[foo]"() {
|
||||
this.assertEquivalent(
|
||||
select("[href]", document.body),
|
||||
select("a[href]", document.body)
|
||||
);
|
||||
this.assertEquivalent(
|
||||
select("[class~=internal]"),
|
||||
select('a[class~="internal"]')
|
||||
);
|
||||
this.assertEquivalent(select("[id]"), select("*[id]"));
|
||||
this.assertEquivalent(
|
||||
select("[type=radio]"),
|
||||
getById("checked_radio", "unchecked_radio")
|
||||
);
|
||||
this.assertEquivalent(
|
||||
select("[type=checkbox]"),
|
||||
select("*[type=checkbox]")
|
||||
);
|
||||
this.assertEquivalent(
|
||||
select("[title]"),
|
||||
getById("with_title", "commaParent")
|
||||
);
|
||||
this.assertEquivalent(
|
||||
select("#troubleForm [type=radio]"),
|
||||
select("#troubleForm *[type=radio]")
|
||||
);
|
||||
this.assertEquivalent(
|
||||
select("#troubleForm [type]"),
|
||||
select("#troubleForm *[type]")
|
||||
);
|
||||
},
|
||||
"E[foo]"() {
|
||||
this.assertEquivalent(
|
||||
select("h1[class]"),
|
||||
select("#fixtures h1"),
|
||||
"h1[class]"
|
||||
);
|
||||
this.assertEquivalent(
|
||||
select("h1[CLASS]"),
|
||||
select("#fixtures h1"),
|
||||
"h1[CLASS]"
|
||||
);
|
||||
this.assertEqual(
|
||||
select("li#item_3[class]")[0],
|
||||
getById("item_3"),
|
||||
"li#item_3[class]"
|
||||
);
|
||||
this.assertEquivalent(
|
||||
select('#troubleForm2 input[name="brackets[5][]"]'),
|
||||
getById("chk_1", "chk_2")
|
||||
);
|
||||
// Brackets in attribute value
|
||||
this.assertEqual(
|
||||
select('#troubleForm2 input[name="brackets[5][]"]:checked')[0],
|
||||
getById("chk_1")
|
||||
);
|
||||
// Space in attribute value
|
||||
this.assertEqual(
|
||||
select('cite[title="hello world!"]')[0],
|
||||
getById("with_title")
|
||||
);
|
||||
// Namespaced attributes
|
||||
// this.assertEquivalent(select('[xml:lang]'), [document.documentElement, getById("item_3")]);
|
||||
// this.assertEquivalent(select('*[xml:lang]'), [document.documentElement, getById("item_3")]);
|
||||
},
|
||||
'E[foo="bar"]'() {
|
||||
this.assertEquivalent(
|
||||
select('a[href="#"]'),
|
||||
getById("link_1", "link_2", "link_3")
|
||||
);
|
||||
this.assertThrowsException(/Error/, () => {
|
||||
select("a[href=#]");
|
||||
});
|
||||
this.assertEqual(
|
||||
select(
|
||||
'#troubleForm2 input[name="brackets[5][]"][value="2"]'
|
||||
)[0],
|
||||
getById("chk_2")
|
||||
);
|
||||
},
|
||||
'E[foo~="bar"]'() {
|
||||
this.assertEquivalent(
|
||||
select('a[class~="internal"]'),
|
||||
getById("link_1", "link_2"),
|
||||
'a[class~="internal"]'
|
||||
);
|
||||
this.assertEquivalent(
|
||||
select("a[class~=internal]"),
|
||||
getById("link_1", "link_2"),
|
||||
"a[class~=internal]"
|
||||
);
|
||||
this.assertEqual(
|
||||
select('a[class~=external][href="#"]')[0],
|
||||
getById("link_3"),
|
||||
'a[class~=external][href="#"]'
|
||||
);
|
||||
} /* 'E[foo|="en"]': function(){
|
||||
this.assertEqual(select('*[xml:lang|="es"]')[0], getById('item_3'));
|
||||
this.assertEqual(select('*[xml:lang|="ES"]')[0], getById('item_3'));
|
||||
},*/,
|
||||
'E[foo^="bar"]'() {
|
||||
this.assertEquivalent(
|
||||
select("div[class^=bro]"),
|
||||
getById("father", "uncle"),
|
||||
"matching beginning of string"
|
||||
);
|
||||
this.assertEquivalent(
|
||||
select('#level1 *[id^="level2_"]'),
|
||||
getById("level2_1", "level2_2", "level2_3")
|
||||
);
|
||||
this.assertEquivalent(
|
||||
select("#level1 *[id^=level2_]"),
|
||||
getById("level2_1", "level2_2", "level2_3")
|
||||
);
|
||||
if (RUN_BENCHMARKS) {
|
||||
this.wait(function () {
|
||||
this.benchmark(() => {
|
||||
select("#level1 *[id^=level2_]");
|
||||
}, 1000);
|
||||
}, 500);
|
||||
}
|
||||
},
|
||||
'E[foo$="bar"]'() {
|
||||
this.assertEquivalent(
|
||||
select("div[class$=men]"),
|
||||
getById("father", "uncle"),
|
||||
"matching end of string"
|
||||
);
|
||||
this.assertEquivalent(
|
||||
select('#level1 *[id$="_1"]'),
|
||||
getById("level2_1", "level3_1")
|
||||
);
|
||||
this.assertEquivalent(
|
||||
select("#level1 *[id$=_1]"),
|
||||
getById("level2_1", "level3_1")
|
||||
);
|
||||
if (RUN_BENCHMARKS) {
|
||||
this.wait(function () {
|
||||
this.benchmark(() => {
|
||||
select("#level1 *[id$=_1]");
|
||||
}, 1000);
|
||||
}, 500);
|
||||
}
|
||||
},
|
||||
'E[foo*="bar"]'() {
|
||||
this.assertEquivalent(
|
||||
select('div[class*="ers m"]'),
|
||||
getById("father", "uncle"),
|
||||
"matching substring"
|
||||
);
|
||||
this.assertEquivalent(
|
||||
select('#level1 *[id*="2"]'),
|
||||
getById("level2_1", "level3_2", "level2_2", "level2_3")
|
||||
);
|
||||
this.assertThrowsException(/Error/, () => {
|
||||
select("#level1 *[id*=2]");
|
||||
});
|
||||
if (RUN_BENCHMARKS) {
|
||||
this.wait(function () {
|
||||
this.benchmark(() => {
|
||||
select("#level1 *[id*=2]");
|
||||
}, 1000);
|
||||
}, 500);
|
||||
}
|
||||
},
|
||||
|
||||
// *** these should throw SYNTAX_ERR ***
|
||||
|
||||
"E[id=-1]"() {
|
||||
this.assertThrowsException(/Error/, () => {
|
||||
select("#level1 *[id=-1]");
|
||||
});
|
||||
if (RUN_BENCHMARKS) {
|
||||
this.wait(function () {
|
||||
this.benchmark(() => {
|
||||
select("#level1 *[id=9]");
|
||||
}, 1000);
|
||||
}, 500);
|
||||
}
|
||||
},
|
||||
"E[class=-45deg]"() {
|
||||
this.assertThrowsException(/Error/, () => {
|
||||
select("#level1 *[class=-45deg]");
|
||||
});
|
||||
if (RUN_BENCHMARKS) {
|
||||
this.wait(function () {
|
||||
this.benchmark(() => {
|
||||
select("#level1 *[class=-45deg]");
|
||||
}, 1000);
|
||||
}, 500);
|
||||
}
|
||||
},
|
||||
"E[class=8mm]"() {
|
||||
this.assertThrowsException(/Error/, () => {
|
||||
select("#level1 *[class=8mm]");
|
||||
});
|
||||
if (RUN_BENCHMARKS) {
|
||||
this.wait(function () {
|
||||
this.benchmark(() => {
|
||||
select("#level1 *[class=8mm]");
|
||||
}, 1000);
|
||||
}, 500);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
runner.addGroup("Structural pseudo-classes").addTests(null, {
|
||||
"E:first-child"() {
|
||||
this.assertEqual(
|
||||
select("#level1>*:first-child")[0],
|
||||
getById("level2_1")
|
||||
);
|
||||
this.assertEquivalent(
|
||||
select("#level1 *:first-child"),
|
||||
getById("level2_1", "level3_1", "level_only_child")
|
||||
);
|
||||
this.assertEquivalent(select("#level1>div:first-child"), []);
|
||||
this.assertEquivalent(
|
||||
select("#level1 span:first-child"),
|
||||
getById("level2_1", "level3_1")
|
||||
);
|
||||
this.assertEquivalent(select("#level1:first-child"), []);
|
||||
if (RUN_BENCHMARKS) {
|
||||
this.wait(function () {
|
||||
this.benchmark(() => {
|
||||
select("#level1 *:first-child");
|
||||
}, 1000);
|
||||
}, 500);
|
||||
}
|
||||
},
|
||||
"E:last-child"() {
|
||||
this.assertEqual(
|
||||
select("#level1>*:last-child")[0],
|
||||
getById("level2_3")
|
||||
);
|
||||
this.assertEquivalent(
|
||||
select("#level1 *:last-child"),
|
||||
getById("level3_2", "level_only_child", "level2_3")
|
||||
);
|
||||
this.assertEqual(
|
||||
select("#level1>div:last-child")[0],
|
||||
getById("level2_3")
|
||||
);
|
||||
this.assertEqual(
|
||||
select("#level1 div:last-child")[0],
|
||||
getById("level2_3")
|
||||
);
|
||||
this.assertEquivalent(select("#level1>span:last-child"), []);
|
||||
if (RUN_BENCHMARKS) {
|
||||
this.wait(function () {
|
||||
this.benchmark(() => {
|
||||
select("#level1 *:last-child");
|
||||
}, 1000);
|
||||
}, 500);
|
||||
}
|
||||
},
|
||||
"E:nth-child(n)"() {
|
||||
this.assertEqual(select("#p *:nth-child(3)")[0], getById("link_2"));
|
||||
this.assertEqual(
|
||||
select("#p a:nth-child(3)")[0],
|
||||
getById("link_2"),
|
||||
"nth-child"
|
||||
);
|
||||
this.assertEquivalent(
|
||||
select("#list > li:nth-child(n+2)"),
|
||||
getById("item_2", "item_3")
|
||||
);
|
||||
this.assertEquivalent(
|
||||
select("#list > li:nth-child(-n+2)"),
|
||||
getById("item_1", "item_2")
|
||||
);
|
||||
},
|
||||
"E:nth-of-type(n)"() {
|
||||
this.assertEqual(
|
||||
select("#p a:nth-of-type(2)")[0],
|
||||
getById("link_2"),
|
||||
"nth-of-type"
|
||||
);
|
||||
this.assertEqual(
|
||||
select("#p a:nth-of-type(1)")[0],
|
||||
getById("link_1"),
|
||||
"nth-of-type"
|
||||
);
|
||||
},
|
||||
"E:nth-last-of-type(n)"() {
|
||||
this.assertEqual(
|
||||
select("#p a:nth-last-of-type(1)")[0],
|
||||
getById("link_2"),
|
||||
"nth-last-of-type"
|
||||
);
|
||||
},
|
||||
"E:first-of-type"() {
|
||||
this.assertEqual(
|
||||
select("#p a:first-of-type")[0],
|
||||
getById("link_1"),
|
||||
"first-of-type"
|
||||
);
|
||||
},
|
||||
"E:last-of-type"() {
|
||||
this.assertEqual(
|
||||
select("#p a:last-of-type")[0],
|
||||
getById("link_2"),
|
||||
"last-of-type"
|
||||
);
|
||||
},
|
||||
"E:only-child"() {
|
||||
this.assertEqual(
|
||||
select("#level1 *:only-child")[0],
|
||||
getById("level_only_child")
|
||||
);
|
||||
// Shouldn't return anything
|
||||
this.assertEquivalent(select("#level1>*:only-child"), []);
|
||||
this.assertEquivalent(select("#level1:only-child"), []);
|
||||
this.assertEquivalent(
|
||||
select("#level2_2 :only-child:not(:last-child)"),
|
||||
[]
|
||||
);
|
||||
this.assertEquivalent(
|
||||
select("#level2_2 :only-child:not(:first-child)"),
|
||||
[]
|
||||
);
|
||||
if (RUN_BENCHMARKS) {
|
||||
this.wait(function () {
|
||||
this.benchmark(() => {
|
||||
select("#level1 *:only-child");
|
||||
}, 1000);
|
||||
}, 500);
|
||||
}
|
||||
},
|
||||
"E:empty"() {
|
||||
getById("level3_1").children = [];
|
||||
if (document.createEvent) {
|
||||
this.assertEquivalent(
|
||||
select("#level1 *:empty"),
|
||||
getById("level3_1", "level3_2", "level2_3"),
|
||||
"#level1 *:empty"
|
||||
);
|
||||
this.assertEquivalent(
|
||||
select("#level_only_child:empty"),
|
||||
[],
|
||||
"newlines count as content!"
|
||||
);
|
||||
} else {
|
||||
this.assertEqual(
|
||||
select("#level3_1:empty")[0],
|
||||
getById("level3_1"),
|
||||
"IE forced empty content!"
|
||||
);
|
||||
// this.skip("IE forced empty content!");
|
||||
}
|
||||
// Shouldn't return anything
|
||||
this.assertEquivalent(select("span:empty > *"), []);
|
||||
},
|
||||
});
|
||||
|
||||
runner.addTests(null, {
|
||||
"E:not(s)"() {
|
||||
// Negation pseudo-class
|
||||
this.assertEquivalent(select('a:not([href="#"])'), []);
|
||||
this.assertEquivalent(select("div.brothers:not(.brothers)"), []);
|
||||
this.assertEquivalent(
|
||||
select('a[class~=external]:not([href="#"])'),
|
||||
[],
|
||||
'a[class~=external][href!="#"]'
|
||||
);
|
||||
this.assertEqual(
|
||||
select("#p a:not(:first-of-type)")[0],
|
||||
getById("link_2"),
|
||||
"first-of-type"
|
||||
);
|
||||
this.assertEqual(
|
||||
select("#p a:not(:last-of-type)")[0],
|
||||
getById("link_1"),
|
||||
"last-of-type"
|
||||
);
|
||||
this.assertEqual(
|
||||
select("#p a:not(:nth-of-type(1))")[0],
|
||||
getById("link_2"),
|
||||
"nth-of-type"
|
||||
);
|
||||
this.assertEqual(
|
||||
select("#p a:not(:nth-last-of-type(1))")[0],
|
||||
getById("link_1"),
|
||||
"nth-last-of-type"
|
||||
);
|
||||
this.assertEqual(
|
||||
select("#p a:not([rel~=nofollow])")[0],
|
||||
getById("link_2"),
|
||||
"attribute 1"
|
||||
);
|
||||
this.assertEqual(
|
||||
select("#p a:not([rel^=external])")[0],
|
||||
getById("link_2"),
|
||||
"attribute 2"
|
||||
);
|
||||
this.assertEqual(
|
||||
select("#p a:not([rel$=nofollow])")[0],
|
||||
getById("link_2"),
|
||||
"attribute 3"
|
||||
);
|
||||
this.assertEqual(
|
||||
select('#p a:not([rel$="nofollow"]) > em')[0],
|
||||
getById("em"),
|
||||
"attribute 4"
|
||||
);
|
||||
this.assertEqual(
|
||||
select("#list li:not(#item_1):not(#item_3)")[0],
|
||||
getById("item_2"),
|
||||
"adjacent :not clauses"
|
||||
);
|
||||
this.assertEqual(
|
||||
select("#grandfather > div:not(#uncle) #son")[0],
|
||||
getById("son")
|
||||
);
|
||||
this.assertEqual(
|
||||
select('#p a:not([rel$="nofollow"]) em')[0],
|
||||
getById("em"),
|
||||
"attribute 4 + all descendants"
|
||||
);
|
||||
this.assertEqual(
|
||||
select('#p a:not([rel$="nofollow"])>em')[0],
|
||||
getById("em"),
|
||||
"attribute 4 (without whitespace)"
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
runner.addGroup("UI element states pseudo-classes").addTests(null, {
|
||||
"E:disabled"() {
|
||||
this.assertEqual(
|
||||
select("#troubleForm > p > *:disabled")[0],
|
||||
getById("disabled_text_field")
|
||||
);
|
||||
},
|
||||
"E:checked"() {
|
||||
this.assertEquivalent(
|
||||
select("#troubleForm *:checked"),
|
||||
getById("checked_box", "checked_radio")
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
runner.addGroup("Combinators").addTests(null, {
|
||||
"E F"() {
|
||||
// Descendant
|
||||
this.assertEquivalent(
|
||||
select("#fixtures a *"),
|
||||
getById("em2", "em", "span")
|
||||
);
|
||||
this.assertEqual(select("div#fixtures p")[0], getById("p"));
|
||||
},
|
||||
"E + F"() {
|
||||
// Adjacent sibling
|
||||
this.assertEqual(
|
||||
select("div.brothers + div.brothers")[0],
|
||||
getById("uncle")
|
||||
);
|
||||
this.assertEqual(select("div.brothers + div")[0], getById("uncle"));
|
||||
this.assertEqual(select("#level2_1+span")[0], getById("level2_2"));
|
||||
this.assertEqual(
|
||||
select("#level2_1 + span")[0],
|
||||
getById("level2_2")
|
||||
);
|
||||
this.assertEqual(select("#level2_1 + *")[0], getById("level2_2"));
|
||||
this.assertEquivalent(select("#level2_2 + span"), []);
|
||||
this.assertEqual(
|
||||
select("#level3_1 + span")[0],
|
||||
getById("level3_2")
|
||||
);
|
||||
this.assertEqual(select("#level3_1 + *")[0], getById("level3_2"));
|
||||
this.assertEquivalent(select("#level3_2 + *"), []);
|
||||
this.assertEquivalent(select("#level3_1 + em"), []);
|
||||
|
||||
this.assertEqual(
|
||||
select("+ div.brothers", select("div.brothers"))[0],
|
||||
getById("uncle")
|
||||
);
|
||||
this.assertEqual(
|
||||
select("+ div", select("div.brothers"))[0],
|
||||
getById("uncle")
|
||||
);
|
||||
this.assertEqual(
|
||||
select("+span", select("#level2_1"))[0],
|
||||
getById("level2_2")
|
||||
);
|
||||
this.assertEqual(
|
||||
select("+ span", select("#level2_1"))[0],
|
||||
getById("level2_2")
|
||||
);
|
||||
this.assertEqual(
|
||||
select("+ *", select("#level2_1"))[0],
|
||||
getById("level2_2")
|
||||
);
|
||||
this.assertEquivalent(select("+ span", select("#level2_2")), []);
|
||||
this.assertEqual(
|
||||
select("+ span", select("#level3_1"))[0],
|
||||
getById("level3_2")
|
||||
);
|
||||
this.assertEqual(
|
||||
select("+ *", select("#level3_1"))[0],
|
||||
getById("level3_2")
|
||||
);
|
||||
this.assertEquivalent(select("+ *", select("#level3_2")), []);
|
||||
this.assertEquivalent(select("+ em", select("#level3_1")), []);
|
||||
|
||||
if (RUN_BENCHMARKS) {
|
||||
this.wait(function () {
|
||||
this.benchmark(() => {
|
||||
select("#level3_1 + span");
|
||||
}, 1000);
|
||||
}, 500);
|
||||
}
|
||||
},
|
||||
"E > F"() {
|
||||
// Child
|
||||
this.assertEquivalent(
|
||||
select("p.first > a"),
|
||||
getById("link_1", "link_2")
|
||||
);
|
||||
this.assertEquivalent(
|
||||
select("div#grandfather > div"),
|
||||
getById("father", "uncle")
|
||||
);
|
||||
this.assertEquivalent(
|
||||
select("#level1>span"),
|
||||
getById("level2_1", "level2_2")
|
||||
);
|
||||
this.assertEquivalent(
|
||||
select("#level1 > span"),
|
||||
getById("level2_1", "level2_2")
|
||||
);
|
||||
this.assertEquivalent(
|
||||
select("#level2_1 > *"),
|
||||
getById("level3_1", "level3_2")
|
||||
);
|
||||
this.assertEquivalent(select("div > #nonexistent"), []);
|
||||
|
||||
this.assertEquivalent(
|
||||
select("> a", select("p.first")),
|
||||
getById("link_1", "link_2")
|
||||
);
|
||||
this.assertEquivalent(
|
||||
select("> div", select("div#grandfather")),
|
||||
getById("father", "uncle")
|
||||
);
|
||||
this.assertEquivalent(
|
||||
select(">span", select("#level1")),
|
||||
getById("level2_1", "level2_2")
|
||||
);
|
||||
this.assertEquivalent(
|
||||
select("> span", select("#level1")),
|
||||
getById("level2_1", "level2_2")
|
||||
);
|
||||
this.assertEquivalent(
|
||||
select("> *", select("#level2_1")),
|
||||
getById("level3_1", "level3_2")
|
||||
);
|
||||
this.assertEquivalent(select("> #nonexistent", select("div")), []);
|
||||
|
||||
if (RUN_BENCHMARKS) {
|
||||
this.wait(function () {
|
||||
this.benchmark(() => {
|
||||
select("#level1 > span");
|
||||
}, 1000);
|
||||
}, 500);
|
||||
}
|
||||
},
|
||||
"E ~ F"() {
|
||||
// General sibling
|
||||
this.assertEqual(select("h1 ~ ul")[0], getById("list"));
|
||||
this.assertEquivalent(select("#level2_2 ~ span"), []);
|
||||
this.assertEquivalent(select("#level3_2 ~ *"), []);
|
||||
this.assertEquivalent(select("#level3_1 ~ em"), []);
|
||||
this.assertEquivalent(select("div ~ #level3_2"), []);
|
||||
this.assertEquivalent(select("div ~ #level2_3"), []);
|
||||
this.assertEqual(
|
||||
select("#level2_1 ~ span")[0],
|
||||
getById("level2_2")
|
||||
);
|
||||
this.assertEquivalent(
|
||||
select("#level2_1 ~ *"),
|
||||
getById("level2_2", "level2_3")
|
||||
);
|
||||
this.assertEqual(
|
||||
select("#level3_1 ~ #level3_2")[0],
|
||||
getById("level3_2")
|
||||
);
|
||||
this.assertEqual(
|
||||
select("span ~ #level3_2")[0],
|
||||
getById("level3_2")
|
||||
);
|
||||
|
||||
this.assertEqual(select("~ ul", select("h1"))[0], getById("list"));
|
||||
this.assertEquivalent(select("~ span", select("#level2_2")), []);
|
||||
this.assertEquivalent(select("~ *", select("#level3_2")), []);
|
||||
this.assertEquivalent(select("~ em", select("#level3_1")), []);
|
||||
this.assertEquivalent(select("~ #level3_2", select("div")), []);
|
||||
this.assertEquivalent(select("~ #level2_3", select("div")), []);
|
||||
this.assertEqual(
|
||||
select("~ span", select("#level2_1"))[0],
|
||||
getById("level2_2")
|
||||
);
|
||||
this.assertEquivalent(
|
||||
select("~ *", select("#level2_1")),
|
||||
getById("level2_2", "level2_3")
|
||||
);
|
||||
this.assertEqual(
|
||||
select("~ #level3_2", select("#level3_1"))[0],
|
||||
getById("level3_2")
|
||||
);
|
||||
this.assertEqual(
|
||||
select("~ #level3_2", select("span"))[0],
|
||||
getById("level3_2")
|
||||
);
|
||||
|
||||
if (RUN_BENCHMARKS) {
|
||||
this.wait(function () {
|
||||
this.benchmark(() => {
|
||||
select("#level2_1 ~ span");
|
||||
}, 1000);
|
||||
}, 500);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
runner.addTests(null, {
|
||||
"NW.Dom.match"() {
|
||||
const element = getById("dupL1");
|
||||
// Assertions
|
||||
this.assert(match(element, "span"));
|
||||
this.assert(match(element, "span#dupL1"));
|
||||
this.assert(match(element, "div > span"), "child combinator");
|
||||
this.assert(
|
||||
match(element, "#dupContainer span"),
|
||||
"descendant combinator"
|
||||
);
|
||||
this.assert(match(element, "#dupL1"), "ID only");
|
||||
this.assert(match(element, "span.span_foo"), "class name 1");
|
||||
this.assert(match(element, "span.span_bar"), "class name 2");
|
||||
this.assert(
|
||||
match(element, "span:first-child"),
|
||||
"first-child pseudoclass"
|
||||
);
|
||||
// Refutations
|
||||
this.refute(match(element, "span.span_wtf"), "bogus class name");
|
||||
this.refute(match(element, "#dupL2"), "different ID");
|
||||
this.refute(match(element, "div"), "different tag name");
|
||||
this.refute(match(element, "span span"), "different ancestry");
|
||||
this.refute(match(element, "span > span"), "different parent");
|
||||
this.refute(
|
||||
match(element, "span:nth-child(5)"),
|
||||
"different pseudoclass"
|
||||
);
|
||||
// Misc.
|
||||
this.refute(match(getById("link_2"), "a[rel^=external]"));
|
||||
this.assert(match(getById("link_1"), "a[rel^=external]"));
|
||||
this.assert(match(getById("link_1"), 'a[rel^="external"]'));
|
||||
this.assert(match(getById("link_1"), "a[rel^='external']"));
|
||||
},
|
||||
"Equivalent Selectors"() {
|
||||
this.assertEquivalent(
|
||||
select("div.brothers"),
|
||||
select("div[class~=brothers]")
|
||||
);
|
||||
this.assertEquivalent(
|
||||
select("div.brothers"),
|
||||
select("div[class~=brothers].brothers")
|
||||
);
|
||||
this.assertEquivalent(
|
||||
select("div:not(.brothers)"),
|
||||
select("div:not([class~=brothers])")
|
||||
);
|
||||
this.assertEquivalent(
|
||||
select("li ~ li"),
|
||||
select("li:not(:first-child)")
|
||||
);
|
||||
this.assertEquivalent(
|
||||
select("ul > li"),
|
||||
select("ul > li:nth-child(n)")
|
||||
);
|
||||
this.assertEquivalent(
|
||||
select("ul > li:nth-child(even)"),
|
||||
select("ul > li:nth-child(2n)")
|
||||
);
|
||||
this.assertEquivalent(
|
||||
select("ul > li:nth-child(odd)"),
|
||||
select("ul > li:nth-child(2n+1)")
|
||||
);
|
||||
this.assertEquivalent(
|
||||
select("ul > li:first-child"),
|
||||
select("ul > li:nth-child(1)")
|
||||
);
|
||||
this.assertEquivalent(
|
||||
select("ul > li:last-child"),
|
||||
select("ul > li:nth-last-child(1)")
|
||||
);
|
||||
/* Opera 10 does not accept values > 128 as a parameter to :nth-child
|
||||
See <http://operawiki.info/ArtificialLimits> */
|
||||
this.assertEquivalent(
|
||||
select("ul > li:nth-child(n-128)"),
|
||||
select("ul > li")
|
||||
);
|
||||
this.assertEquivalent(select("ul>li"), select("ul > li"));
|
||||
this.assertEquivalent(
|
||||
select('#p a:not([rel$="nofollow"])>em'),
|
||||
select('#p a:not([rel$="nofollow"]) > em')
|
||||
);
|
||||
},
|
||||
"Multiple Selectors"() {
|
||||
// The next two assertions should return document-ordered lists of matching elements --Diego Perini
|
||||
// this.assertEquivalent(select('#list, .first,*[xml:lang="es-us"] , #troubleForm'), getById('p', 'link_1', 'list', 'item_1', 'item_3', 'troubleForm'));
|
||||
// this.assertEquivalent(select('#list, .first, *[xml:lang="es-us"], #troubleForm'), getById('p', 'link_1', 'list', 'item_1', 'item_3', 'troubleForm'));
|
||||
this.assertEquivalent(
|
||||
select(
|
||||
'form[title*="commas,"], input[value="#commaOne,#commaTwo"]'
|
||||
),
|
||||
getById("commaParent", "commaChild")
|
||||
);
|
||||
this.assertEquivalent(
|
||||
select(
|
||||
'form[title*="commas,"], input[value="#commaOne,#commaTwo"]'
|
||||
),
|
||||
getById("commaParent", "commaChild")
|
||||
);
|
||||
},
|
||||
});
|
||||
})(runner);
|
File diff suppressed because it is too large
Load Diff
@ -1,55 +0,0 @@
|
||||
const assert = require("assert");
|
||||
const helper = require("../../tools/helper");
|
||||
const CSSselect = require("../../../src").default;
|
||||
const path = require("path");
|
||||
const docPath = path.join(__dirname, "index.html");
|
||||
let document = null;
|
||||
|
||||
function loadDoc() {
|
||||
return (document = helper.getDocument(docPath));
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
q,
|
||||
t,
|
||||
loadDoc,
|
||||
createWithFriesXML,
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns an array of elements with the given IDs
|
||||
* @example q("main", "foo", "bar")
|
||||
* @result [<div id="main">, <span id="foo">, <input id="bar">]
|
||||
*/
|
||||
function q(...ids) {
|
||||
return ids.map((id) => document.getElementById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that a select matches the given IDs
|
||||
* @param {String} assertionName - Assertion name
|
||||
* @param {String} sizzleSelector - Sizzle selector
|
||||
* @param {String} expectedIds - Array of ids to construct what is expected
|
||||
* @example t("Check for something", "//[a]", ["foo", "baar"]);
|
||||
* @returns `true` iff the selector produces the expected elements.
|
||||
*/
|
||||
function t(assertionName, sizzleSelector, expectedIds) {
|
||||
const actual = CSSselect(sizzleSelector, document);
|
||||
const actualIds = actual.map((e) => e.attribs.id);
|
||||
|
||||
assert.deepStrictEqual(
|
||||
actualIds,
|
||||
expectedIds,
|
||||
`${assertionName} (${sizzleSelector})`
|
||||
);
|
||||
}
|
||||
|
||||
const xmlDoc = helper.getDOMFromPath(path.join(__dirname, "fries.xml"), {
|
||||
xmlMode: true,
|
||||
});
|
||||
const filtered = xmlDoc.filter((t) => t.type === "tag");
|
||||
xmlDoc.lastChild = filtered[filtered.length - 1];
|
||||
|
||||
function createWithFriesXML() {
|
||||
return xmlDoc;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,19 +0,0 @@
|
||||
describe("nwmatcher", () => require("./nwmatcher/"));
|
||||
|
||||
describe("sizzle", () => {
|
||||
describe("selector", () => {
|
||||
require("./sizzle/selector");
|
||||
});
|
||||
});
|
||||
|
||||
describe("qwery", () => exportsRun(require("./qwery/")));
|
||||
|
||||
function exportsRun(mod) {
|
||||
for (const [name, suite] of Object.entries(mod)) {
|
||||
if (typeof suite === "object") {
|
||||
describe(name, () => exportsRun(suite));
|
||||
} else {
|
||||
it(name, suite);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
const ben = require("ben");
|
||||
const testString =
|
||||
'doo, *#foo > elem.bar[class$=bAz i]:not([ id *= "2" ]):nth-child(2n)';
|
||||
const helper = require("./helper");
|
||||
const CSSselect = require("../../src");
|
||||
const dom = helper.getDefaultDom();
|
||||
|
||||
// console.log("Parsing took:", ben(1e5, function(){compile(testString);}));
|
||||
const compiled = CSSselect.compile(testString);
|
||||
console.log(
|
||||
"Executing took:",
|
||||
ben(1e6, () => CSSselect.selectAll(compiled, dom)) * 1e3
|
||||
);
|
File diff suppressed because it is too large
Load Diff
@ -1,47 +0,0 @@
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const htmlparser2 = require("htmlparser2");
|
||||
const DomUtils = require("domutils");
|
||||
|
||||
function getDOMFromPath(path, options) {
|
||||
return htmlparser2.parseDOM(fs.readFileSync(path).toString(), options);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getFile(name, options) {
|
||||
return getDOMFromPath(path.join(__dirname, "docs", name), options);
|
||||
},
|
||||
getDOMFromPath,
|
||||
getDOM: htmlparser2.parseDOM,
|
||||
getDefaultDom() {
|
||||
return htmlparser2.parseDOM(
|
||||
"<elem id=foo><elem class='bar baz'><tag class='boom'> This is some simple text </tag></elem></elem>"
|
||||
);
|
||||
},
|
||||
getDocument(path) {
|
||||
const document = getDOMFromPath(path);
|
||||
|
||||
document.getElementsByTagName = (name = "*") =>
|
||||
DomUtils.getElementsByTagName(name, document);
|
||||
document.getElementById = (id) => DomUtils.getElementById(id, document);
|
||||
document.createTextNode = (content) => ({
|
||||
type: "text",
|
||||
data: content,
|
||||
});
|
||||
document.createElement = (name) => ({
|
||||
type: "tag",
|
||||
name,
|
||||
children: [],
|
||||
attribs: {},
|
||||
});
|
||||
[document.body] = DomUtils.getElementsByTagName(
|
||||
"body",
|
||||
document,
|
||||
true,
|
||||
1
|
||||
);
|
||||
[document.documentElement] = document.filter(DomUtils.isTag);
|
||||
|
||||
return document;
|
||||
},
|
||||
};
|
@ -0,0 +1,48 @@
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import * as htmlparser2 from "htmlparser2";
|
||||
import * as DomUtils from "domutils";
|
||||
import { DataNode, Element, Node } from "domhandler";
|
||||
|
||||
export function getDOMFromPath(
|
||||
file: string,
|
||||
options?: htmlparser2.ParserOptions
|
||||
): Node[] {
|
||||
const filePath = path.join(__dirname, "..", "fixtures", file);
|
||||
return htmlparser2.parseDOM(fs.readFileSync(filePath, "utf8"), options);
|
||||
}
|
||||
|
||||
export function getFile(
|
||||
name: string,
|
||||
options?: htmlparser2.ParserOptions
|
||||
): Node[] {
|
||||
return getDOMFromPath(path.join(__dirname, "docs", name), options);
|
||||
}
|
||||
|
||||
export const getDOM = htmlparser2.parseDOM;
|
||||
|
||||
export interface SimpleDocument extends Array<Node> {
|
||||
getElementsByTagName(name: string): Element[];
|
||||
getElementById(id: string): Element;
|
||||
createTextNode(content: string): DataNode;
|
||||
createElement(name: string): Element;
|
||||
body: Element;
|
||||
documentElement: Element;
|
||||
}
|
||||
|
||||
export function getDocument(file: string): SimpleDocument {
|
||||
const document = getDOMFromPath(file) as SimpleDocument;
|
||||
|
||||
document.getElementsByTagName = (name = "*") =>
|
||||
DomUtils.getElementsByTagName(name, document, true);
|
||||
document.getElementById = (id: string) =>
|
||||
DomUtils.getElementById(id, document) as Element;
|
||||
document.createTextNode = (content: string) =>
|
||||
new DataNode(htmlparser2.ElementType.Text, content);
|
||||
document.createElement = (name: string) =>
|
||||
new Element(name.toLocaleLowerCase(), {});
|
||||
[document.body] = DomUtils.getElementsByTagName("body", document, true, 1);
|
||||
[document.documentElement] = document.filter(DomUtils.isTag);
|
||||
|
||||
return document;
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
import * as assert from "assert";
|
||||
import * as helper from "./helper";
|
||||
import CSSselect from "../../src";
|
||||
import type { Element, Node } from "domhandler";
|
||||
let document = loadDoc();
|
||||
|
||||
export function loadDoc(): helper.SimpleDocument {
|
||||
return (document = helper.getDocument("sizzle.html"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of elements with the given IDs
|
||||
* @example q("main", "foo", "bar")
|
||||
* @result [<div id="main">, <span id="foo">, <input id="bar">]
|
||||
*/
|
||||
export function q(...ids: string[]): Element[] {
|
||||
return ids.map((id) => document.getElementById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that a select matches the given IDs
|
||||
* @param assertionName - Assertion name
|
||||
* @param sizzleSelector - Sizzle selector
|
||||
* @param expectedIds - Array of ids to construct what is expected
|
||||
* @example t("Check for something", "//[a]", ["foo", "baar"]);
|
||||
* @returns `true` iff the selector produces the expected elements.
|
||||
*/
|
||||
export function t(
|
||||
assertionName: string,
|
||||
sizzleSelector: string,
|
||||
expectedIds: string[]
|
||||
): void {
|
||||
const actual = CSSselect(sizzleSelector, document) as Element[];
|
||||
const actualIds = actual.map((e) => e.attribs.id);
|
||||
|
||||
assert.deepStrictEqual(
|
||||
actualIds,
|
||||
expectedIds,
|
||||
`${assertionName} (${sizzleSelector})`
|
||||
);
|
||||
}
|
||||
|
||||
const xmlDoc = helper.getDOMFromPath("fries.xml", {
|
||||
xmlMode: true,
|
||||
});
|
||||
|
||||
export function createWithFriesXML(): Node[] {
|
||||
return xmlDoc;
|
||||
}
|
@ -1,112 +0,0 @@
|
||||
const helper = require("./helper");
|
||||
const doc = helper.getFile("W3C_Selectors.html");
|
||||
const CSSselect = require("../../src");
|
||||
const soupselect = require("cheerio-soupselect");
|
||||
const selectors = [
|
||||
"body",
|
||||
"div",
|
||||
"body div",
|
||||
"div p",
|
||||
"div > p",
|
||||
"div + p",
|
||||
"div ~ p",
|
||||
"div[class^=exa][class$=mple]",
|
||||
"div p a",
|
||||
"div, p, a",
|
||||
".note",
|
||||
"div.example",
|
||||
"ul .tocline2",
|
||||
"div.example, div.note",
|
||||
"#title",
|
||||
"h1#title",
|
||||
"div #title",
|
||||
"ul.toc li.tocline2",
|
||||
"ul.toc > li.tocline2",
|
||||
"h1#title + div > p",
|
||||
"h1[id]:contains(Selectors)",
|
||||
"a[href][lang][class]",
|
||||
"div[class]",
|
||||
"div[class=example]",
|
||||
"div[class^=exa]",
|
||||
"div[class$=mple]",
|
||||
"div[class*=e]",
|
||||
"div[class|=dialog]",
|
||||
"div[class!=made_up]",
|
||||
"div[class~=example]" /* , "div:not(.example)", "p:contains(selectors)", "p:nth-child(even)", "p:nth-child(2n)", "p:nth-child(odd)", "p:nth-child(2n+1)", "p:nth-child(n)", "p:only-child", "p:last-child", "p:first-child"*/,
|
||||
];
|
||||
|
||||
const engines = [(a, b) => CSSselect(b, a), soupselect.select];
|
||||
|
||||
// returns true when an error occurs
|
||||
function testResult(rule) {
|
||||
const results = engines.map((func) => func(doc, rule));
|
||||
|
||||
// check if both had the same result
|
||||
for (let i = 1; i < results.length; i++) {
|
||||
// TODO: might be hard to debug with more engines
|
||||
if (results[i - 1].length !== results[i].length) {
|
||||
// console.log(rule, results[i-1].length, results[i].length);
|
||||
return true;
|
||||
}
|
||||
for (let j = 0; j < results[i].length; j++) {
|
||||
if (results[i - 1][j] !== results[i][j]) {
|
||||
if (!results[i - 1].includes(results[i][j])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// require("assert").deepEqual(results[i-1], results[i], rule + ": not the same elements");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
selectors.filter(testResult).forEach((rule) => {
|
||||
print(rule, "failed!\n");
|
||||
});
|
||||
|
||||
process.exit(0); // don't run speed tests
|
||||
|
||||
print("-----\n\nChecking performance\n\n");
|
||||
|
||||
// test the speed
|
||||
const ben = require("ben");
|
||||
|
||||
function testSpeed(rule) {
|
||||
print(rule.padEnd(28));
|
||||
|
||||
let results = engines.map((func) => () => func(doc, rule));
|
||||
|
||||
// also add a precompiled CSSselect test
|
||||
const compiled = CSSselect.selectAll(rule);
|
||||
results.unshift(() => CSSselect.iterate(compiled, doc));
|
||||
|
||||
results = results.map(ben);
|
||||
|
||||
const min = Math.min.apply(null, results);
|
||||
const max = Math.max.apply(null, results);
|
||||
|
||||
results.forEach((result) => {
|
||||
if (result === min) return print(" +", result, "+");
|
||||
if (result === max) return print(" !", result, "!");
|
||||
if (Math.abs(result - min) > Math.abs(result - max)) {
|
||||
return print(" =", result, "=");
|
||||
}
|
||||
print(" ~", result, "~");
|
||||
});
|
||||
|
||||
print("\n");
|
||||
}
|
||||
|
||||
print(
|
||||
"RULE ",
|
||||
"CSSselect (pc)",
|
||||
"CSSselect",
|
||||
"soupselect\n"
|
||||
);
|
||||
|
||||
selectors.forEach(testSpeed);
|
||||
|
||||
function print() {
|
||||
process.stdout.write(Array.prototype.join.call(arguments, " "));
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"include": ["src"],
|
||||
"include": ["src", "test"],
|
||||
"exclude": []
|
||||
}
|
||||
|
Loading…
Reference in new issue