refactor(tests): Port tests to TS

And remove some unused benchmarks
pull/232/head
Felix Böhm 4 years ago
parent 7c3c405f04
commit 87afd4acd0

@ -0,0 +1,4 @@
node_modules/
coverage/
lib/
.nyc_output/

@ -1,4 +1,5 @@
{
"extension": ["js", "ts"],
"require": "ts-node/register",
"check-leaks": true,
"reporter": "spec"

26
package-lock.json generated

@ -365,12 +365,24 @@
"integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==",
"dev": true
},
"@types/expect.js": {
"version": "0.3.29",
"resolved": "https://registry.npmjs.org/@types/expect.js/-/expect.js-0.3.29.tgz",
"integrity": "sha1-KN01kVW4S47LCUr8P0t0wyItyjs=",
"dev": true
},
"@types/json-schema": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz",
"integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==",
"dev": true
},
"@types/mocha": {
"version": "8.0.3",
"resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.0.3.tgz",
"integrity": "sha512-vyxR57nv8NfcU0GZu8EUXZLTbCMupIUwy95LJ6lllN+JRPG25CwMHoB1q5xKh8YKhQnHYRAn4yW2yuHbf/5xgg==",
"dev": true
},
"@types/node": {
"version": "14.11.2",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.11.2.tgz",
@ -1150,9 +1162,9 @@
"dev": true
},
"eslint": {
"version": "7.9.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-7.9.0.tgz",
"integrity": "sha512-V6QyhX21+uXp4T+3nrNfI3hQNBDa/P8ga7LoQOenwrlEFXrEnUEE+ok1dMtaS3b6rmLXhT1TkTIsG75HMLbknA==",
"version": "7.10.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-7.10.0.tgz",
"integrity": "sha512-BDVffmqWl7JJXqCjAK6lWtcQThZB/aP1HXSH1JKwGwv0LQEdvpR7qzNrUT487RM39B5goWuboFad5ovMBmD8yA==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.0.0",
@ -1163,7 +1175,7 @@
"debug": "^4.0.1",
"doctrine": "^3.0.0",
"enquirer": "^2.3.5",
"eslint-scope": "^5.1.0",
"eslint-scope": "^5.1.1",
"eslint-utils": "^2.1.0",
"eslint-visitor-keys": "^1.3.0",
"espree": "^7.3.0",
@ -1209,9 +1221,9 @@
}
},
"eslint-config-prettier": {
"version": "6.11.0",
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.11.0.tgz",
"integrity": "sha512-oB8cpLWSAjOVFEJhhyMZh6NOEOtBVziaqdDQ86+qhDHFbZXoRTM7pNSvFRfW/W/L/LrQ38C99J5CGuRBBzBsdA==",
"version": "6.12.0",
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.12.0.tgz",
"integrity": "sha512-9jWPlFlgNwRUYVoujvWTQ1aMO8o6648r+K7qU7K5Jmkbyqav1fuEZC0COYpGBxyiAJb65Ra9hrmFx19xRGwXWw==",
"dev": true,
"requires": {
"get-stdin": "^6.0.0"

@ -23,6 +23,8 @@
"nth-check": "^1.0.2"
},
"devDependencies": {
"@types/expect.js": "^0.3.29",
"@types/mocha": "^8.0.3",
"@types/node": "^14.0.5",
"@typescript-eslint/eslint-plugin": "^4.1.0",
"@typescript-eslint/parser": "^4.1.0",

@ -126,7 +126,7 @@ export const selectAll = getSelectorFunc(
query: Predicate<ElementNode>,
elems: Node[] | null,
options: InternalOptions<Node, ElementNode>
) =>
): ElementNode[] =>
query === falseFunc || !elems || elems.length === 0
? []
: options.adapter.findAll(query, elems)
@ -146,7 +146,7 @@ export const selectOne = getSelectorFunc(
query: Predicate<ElementNode>,
elems: Node[] | null,
options: InternalOptions<Node, ElementNode>
) =>
): ElementNode | null =>
query === falseFunc || !elems || elems.length === 0
? null
: options.adapter.findOne(query, elems)

@ -117,7 +117,7 @@ export interface Options<Node, ElementNode extends Node> {
/**
* The context of the current query. Used to
*/
context?: ElementNode[];
context?: ElementNode | ElementNode[];
}
// Internally, we want to ensure that no propterties are accessed on the passed objects

@ -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,10 +1,13 @@
const CSSselect = require("../src");
const makeDom = require("htmlparser2").parseDOM;
const { trueFunc, falseFunc } = require("boolbase");
const assert = require("assert");
import * as CSSselect from "../src";
import { parseDOM as makeDom } from "htmlparser2";
import { trueFunc, falseFunc } from "boolbase";
import assert from "assert";
import type { Element } from "domhandler";
const [dom] = makeDom("<div id=foo><p>foo</p></div>");
const [xmlDom] = makeDom("<DiV id=foo><P>foo</P></DiV>", { xmlMode: true });
const [dom] = makeDom("<div id=foo><p>foo</p></div>") as Element[];
const [xmlDom] = makeDom("<DiV id=foo><P>foo</P></DiV>", {
xmlMode: true,
}) as Element[];
describe("API", () => {
describe("removes duplicates", () => {
@ -41,7 +44,7 @@ describe("API", () => {
it("should support pseudos led by a traversal (#111)", () => {
const [dom] = makeDom(
'<div><div class="foo">a</div><div class="bar">b</div></div>'
);
) as Element[];
const a = CSSselect.selectAll(".foo:has(+.bar)", dom);
assert.strictEqual(a.length, 1);
assert.strictEqual(a[0], dom.children[0]);

@ -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;
}

@ -17,7 +17,7 @@
<div id="container">
<div id="testlog" class="log"></div>
<!-- Test elements -->
<div id="fixtures" style="display: none;">
<div id="fixtures" style="display: none">
<h1 class="title">Some title <span>here</span></h1>
<p id="p" class="first summary">
<strong id="strong">This</strong> is a short blurb

@ -143,7 +143,7 @@
<ol id="tests"></ol>
<iframe
id="frame"
style="width: 0; height: 0; margin-left: -1000px;"
style="width: 0; height: 0; margin-left: -1000px"
></iframe>
<script src="tests.js"></script>
</body>

@ -0,0 +1,461 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html
xmlns="http://www.w3.org/1999/xhtml"
xml:lang="en"
lang="en"
dir="ltr"
id="html"
>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Sizzle Test Suite</title>
<link
rel="Stylesheet"
media="screen"
href="../bower_components/qunit/qunit/qunit.css"
/>
<script
type="text/javascript"
src="../bower_components/qunit/qunit/qunit.js"
></script>
<script type="text/javascript" src="data/testinit.js"></script>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="../dist/sizzle.js"></script>
<script type="text/javascript" src="unit/selector.js"></script>
<script type="text/javascript" src="unit/utilities.js"></script>
<script type="text/javascript" src="unit/extending.js"></script>
</head>
<body id="body">
<div id="qunit">
<h1 id="qunit-header">jQuery Test Suite</h1>
<h2 id="qunit-banner"></h2>
<div id="qunit-testrunner-toolbar"></div>
<h2 id="qunit-userAgent"></h2>
</div>
<!-- Test HTML -->
<dl
id="dl"
style="
position: absolute;
top: -32767px;
left: -32767px;
width: 1px;
"
>
<div id="qunit-fixture">
<p id="firstp">
See
<a
id="simon1"
href="http://simon.incutio.com/archive/2003/03/25/#getElementsBySelector"
rel="bookmark"
>this blog entry</a
>
for more information.
</p>
<p id="ap">
Here are some [links] in a normal paragraph:
<a id="google" href="http://www.google.com/" title="Google!"
>Google</a
>,
<a
id="groups"
href="http://groups.google.com/"
class="GROUPS"
>Google Groups (Link)</a
>. This link has
<code id="code1"
><a href="http://smin" id="anchor1"
>class="blog"</a
></code
>:
<a
href="http://diveintomark.org/"
class="blog"
hreflang="en"
id="mark"
>diveintomark</a
>
</p>
<div id="foo">
<p id="sndp">
Everything inside the red border is inside a div with
<code>id="foo"</code>.
</p>
<p lang="en" id="en">
This is a normal link:
<a
id="yahoo"
href="http://www.yahoo.com/"
class="blogTest"
>Yahoo</a
>
</p>
<p id="sap">
This link has
<code><a href="#2" id="anchor2">class="blog"</a></code
>:
<a
href="http://simon.incutio.com/"
class="blog link"
id="simon"
>Simon Willison's Weblog</a
>
</p>
</div>
<div
id="nothiddendiv"
style="height: 1px; background: white"
class="nothiddendiv"
>
<div id="nothiddendivchild"></div>
</div>
<span id="name+value"></span>
<p id="first">Try them out:</p>
<ul id="firstUL"></ul>
<!-- prettier-ignore -->
<ol id="empty"><!-- comment --></ol>
<form id="form" action="formaction">
<label for="action" id="label-for">Action:</label>
<input
type="text"
name="action"
value="Test"
id="text1"
maxlength="30"
/>
<input
type="text"
name="text2"
value="Test"
id="text2"
disabled="disabled"
/>
<input type="radio" name="radio1" id="radio1" value="on" />
<input
type="radio"
name="radio2"
id="radio2"
checked="checked"
/>
<input
type="checkbox"
name="check"
id="check1"
checked="checked"
/>
<input type="checkbox" id="check2" value="on" />
<input type="hidden" name="hidden" id="hidden1" />
<input
type="text"
style="display: none"
name="foo[bar]"
id="hidden2"
/>
<input type="text" id="name" name="name" value="name" />
<input
type="search"
id="search"
name="search"
value="search"
/>
<button id="button" name="button" type="button">
Button
</button>
<textarea id="area1" maxlength="30">foobar</textarea>
<select name="select1" id="select1">
<option id="option1a" class="emptyopt" value="">
Nothing
</option>
<option id="option1b" value="1">1</option>
<option id="option1c" value="2">2</option>
<option id="option1d" value="3">3</option>
</select>
<select name="select2" id="select2">
<option id="option2a" class="emptyopt" value="">
Nothing
</option>
<option id="option2b" value="1">1</option>
<option id="option2c" value="2">2</option>
<option id="option2d" selected="selected" value="3">
3
</option>
</select>
<select name="select3" id="select3" multiple="multiple">
<option id="option3a" class="emptyopt" value="">
Nothing
</option>
<option id="option3b" selected="selected" value="1">
1
</option>
<option id="option3c" selected="selected" value="2">
2
</option>
<option id="option3d" value="3">3</option>
<option id="option3e">no value</option>
</select>
<select name="select4" id="select4" multiple="multiple">
<optgroup disabled="disabled">
<option id="option4a" class="emptyopt" value="">
Nothing
</option>
<option
id="option4b"
disabled="disabled"
selected="selected"
value="1"
>
1
</option>
<option id="option4c" selected="selected" value="2">
2
</option>
</optgroup>
<option
selected="selected"
disabled="disabled"
id="option4d"
value="3"
>
3
</option>
<option id="option4e">no value</option>
</select>
<select name="select5" id="select5">
<option id="option5a" value="3">1</option>
<option id="option5b" value="2">2</option>
<option id="option5c" value="1">3</option>
</select>
<object id="object1" codebase="stupid">
<param name="p1" value="x1" />
<param name="p2" value="x2" />
</object>
<span id="台北Táiběi"></span>
<span id="台北" lang="中文"></span>
<span id="utf8class1" class="台北Táiběi 台北"></span>
<span id="utf8class2" class="台北"></span>
<span id="foo:bar" class="foo:bar"
><span id="foo_descendent"></span
></span>
<span id="test.foo[5]bar" class="test.foo[5]bar"></span>
<foo_bar id="foobar">test element</foo_bar>
</form>
<b id="floatTest">Float test.</b>
<iframe id="iframe" name="iframe"></iframe>
<form id="lengthtest">
<input type="text" id="length" name="test" />
<input type="text" id="idTest" name="id" />
</form>
<table id="table"></table>
<form id="name-tests">
<!-- Inputs with a grouped name attribute. -->
<input
name="types[]"
id="types_all"
type="checkbox"
value="all"
/>
<input
name="types[]"
id="types_anime"
type="checkbox"
value="anime"
/>
<input
name="types[]"
id="types_movie"
type="checkbox"
value="movie"
/>
</form>
<form id="testForm" action="#" method="get">
<textarea name="T3" rows="2" cols="15">
?
Z</textarea
>
<input type="hidden" name="H1" value="x" />
<input type="hidden" name="H2" />
<input name="PWD" type="password" value="" />
<input name="T1" type="text" />
<input
name="T2"
type="text"
value="YES"
readonly="readonly"
/>
<input type="checkbox" name="C1" value="1" />
<input type="checkbox" name="C2" />
<input type="radio" name="R1" value="1" />
<input type="radio" name="R1" value="2" />
<input type="text" name="My Name" value="me" />
<input type="reset" name="reset" value="NO" />
<select name="S1">
<option value="abc">ABC</option>
<option value="abc">ABC</option>
<option value="abc">ABC</option>
</select>
<select name="S2" multiple="multiple" size="3">
<option value="abc">ABC</option>
<option value="abc">ABC</option>
<option value="abc">ABC</option>
</select>
<select name="S3">
<option selected="selected">YES</option>
</select>
<select name="S4">
<option value="" selected="selected">NO</option>
</select>
<input type="submit" name="sub1" value="NO" />
<input type="submit" name="sub2" value="NO" />
<input type="image" name="sub3" value="NO" />
<button name="sub4" type="submit" value="NO">NO</button>
<input
name="D1"
type="text"
value="NO"
disabled="disabled"
/>
<input
type="checkbox"
checked="checked"
disabled="disabled"
name="D2"
value="NO"
/>
<input
type="radio"
name="D3"
value="NO"
checked="checked"
disabled="disabled"
/>
<select name="D4" disabled="disabled">
<option selected="selected" value="NO">NO</option>
</select>
<input id="list-test" type="text" />
<datalist id="datalist">
<option value="option"></option>
</datalist>
</form>
<div id="moretests">
<form>
<div id="checkedtest" style="display: none">
<input
type="radio"
name="checkedtestradios"
checked="checked"
/>
<input
type="radio"
name="checkedtestradios"
value="on"
/>
<input
type="checkbox"
name="checkedtestcheckboxes"
checked="checked"
/>
<input
type="checkbox"
name="checkedtestcheckboxes"
/>
</div>
</form>
<div id="nonnodes">
<span>hi</span> there
<!-- mon ami -->
</div>
<div id="t2037">
<div><div class="hidden">hidden</div></div>
</div>
<div id="t6652">
<div></div>
</div>
<div id="t12087">
<input type="hidden" id="el12087" data-comma="0,1" />
</div>
<div id="no-clone-exception">
<object><embed /></object>
</div>
<div id="names-group">
<span id="name-is-example" name="example"></span>
<span id="name-is-div" name="div"></span>
</div>
<script id="script-no-src"></script>
<script id="script-src" src="http://src.test/path"></script>
<div id="id-name-tests">
<a id="tName1ID" name="tName1"><span></span></a>
<a id="tName2ID" name="tName2"><span></span></a>
<div id="tName1"><span id="tName1-span">C</span></div>
</div>
</div>
<div id="tabindex-tests">
<ol id="listWithTabIndex" tabindex="5">
<li id="foodWithNegativeTabIndex" tabindex="-1">
Rice
</li>
<li id="foodNoTabIndex">Beans</li>
<li>Blinis</li>
<li>Tofu</li>
</ol>
<div id="divWithNoTabIndex">I'm hungry. I should...</div>
<span>...</span
><a href="#" id="linkWithNoTabIndex">Eat lots of food</a
><span>...</span> | <span>...</span
><a href="#" id="linkWithTabIndex" tabindex="2"
>Eat a little food</a
><span>...</span> | <span>...</span
><a href="#" id="linkWithNegativeTabIndex" tabindex="-1"
>Eat no food</a
><span>...</span> <span>...</span
><a id="linkWithNoHrefWithNoTabIndex">Eat a burger</a
><span>...</span> <span>...</span
><a id="linkWithNoHrefWithTabIndex" tabindex="1"
>Eat some funyuns</a
><span>...</span> <span>...</span
><a id="linkWithNoHrefWithNegativeTabIndex" tabindex="-1"
>Eat some funyuns</a
><span>...</span>
</div>
<div id="liveHandlerOrder">
<span id="liveSpan1"><a href="#" id="liveLink1"></a></span>
<span id="liveSpan2"><a href="#" id="liveLink2"></a></span>
</div>
<div id="siblingTest">
<em id="siblingfirst">1</em>
<em id="siblingnext">2</em>
<em id="siblingthird">
<em id="siblingchild">
<em id="siblinggrandchild">
<em id="siblinggreatgrandchild"></em>
</em>
</em>
</em>
<span id="siblingspan"></span>
</div>
</div>
</dl>
<div id="scopeTest">
<label id="scopeTest--child"></label>
</div>
<br id="last" />
</body>
</html>

@ -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);

@ -1,16 +1,16 @@
"use strict";
const expect = require("expect.js");
const { DomUtils } = require("htmlparser2");
const helper = require("../tools/helper.js");
const path = require("path");
const document = helper.getDocument(path.join(__dirname, "index.html"));
const CSSselect = require("../../src");
import expect from "expect.js";
import { DomUtils } from "htmlparser2";
import * as helper from "./tools/helper";
const document = helper.getDocument("qwery.html");
import * as CSSselect from "../src";
import type { Node, Element } from "domhandler";
const location = { hash: "" };
CSSselect.pseudos.target = (elem) =>
elem.attribs && elem.attribs.id === location.hash.substr(1);
CSSselect.pseudos.target = (elem, { adapter }) =>
adapter.getAttributeValue(elem, "id") === location.hash.substr(1);
const getElementsByTagName = (id: string, document: Node | Node[]) =>
DomUtils.getElementsByTagName(id, document, true);
// ---
/*
@ -43,13 +43,14 @@ const doc = helper.getDOM(
const el = DomUtils.getElementById("attr-child-boosh", document);
const pseudos = DomUtils.getElementById("pseudos", document).children.filter(
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const pseudos = DomUtils.getElementById("pseudos", document)!.children.filter(
DomUtils.isTag
);
module.exports = {
Contexts: {
"should be able to pass optional context"() {
describe("qwery", () => {
describe("Contexts", () => {
it("should be able to pass optional context", () => {
expect(CSSselect.selectAll(".a", document)).to.have.length(3); // no context found 3 elements (.a)
expect(
CSSselect.selectAll(
@ -57,36 +58,45 @@ module.exports = {
CSSselect.selectAll("#boosh", document)
)
).to.have.length(2); // context found 2 elements (#boosh .a)
},
/*
'should be able to pass string as context': function() {
expect(CSSselect.selectAll('.a', '#boosh')).to.have.length(2); //context found 2 elements(.a, #boosh)
expect(CSSselect.selectAll('.a', '.a')).to.be.empty(); //context found 0 elements(.a, .a)
expect(CSSselect.selectAll('.a', '.b')).to.have.length(1); //context found 1 elements(.a, .b)
expect(CSSselect.selectAll('.a', '#boosh .b')).to.have.length(1); //context found 1 elements(.a, #boosh .b)
expect(CSSselect.selectAll('.b', '#boosh .b')).to.be.empty(); //context found 0 elements(.b, #boosh .b)
},
*/
/*
'should be able to pass qwery result as context': function() {
expect(CSSselect.selectAll('.a', CSSselect.selectAll('#boosh', document))).to.have.length(2); //context found 2 elements(.a, #boosh)
expect(CSSselect.selectAll('.a', CSSselect.selectAll('.a', document))).to.be.empty(); //context found 0 elements(.a, .a)
expect(CSSselect.selectAll('.a', CSSselect.selectAll('.b', document))).to.have.length(1); //context found 1 elements(.a, .b)
expect(CSSselect.selectAll('.a', CSSselect.selectAll('#boosh .b', document))).to.have.length(1); //context found 1 elements(.a, #boosh .b)
expect(CSSselect.selectAll('.b', CSSselect.selectAll('#boosh .b', document))).to.be.empty(); //context found 0 elements(.b, #boosh .b)
},
*/
"should not return duplicates from combinators"() {
});
it.skip("should be able to pass qwery result as context", () => {
expect(
CSSselect.selectAll(
".a",
CSSselect.selectAll("#boosh", document)
)
).to.have.length(2); // context found 2 elements(.a, #boosh)
expect(
CSSselect.selectAll(".a", CSSselect.selectAll(".a", document))
).to.be.empty(); // context found 0 elements(.a, .a)
expect(
CSSselect.selectAll(".a", CSSselect.selectAll(".b", document))
).to.have.length(1); // context found 1 elements(.a, .b)
expect(
CSSselect.selectAll(
".a",
CSSselect.selectAll("#boosh .b", document)
)
).to.have.length(1); // context found 1 elements(.a, #boosh .b)
expect(
CSSselect.selectAll(
".b",
CSSselect.selectAll("#boosh .b", document)
)
).to.be.empty(); // context found 0 elements(.b, #boosh .b)
});
it("should not return duplicates from combinators", () => {
expect(
CSSselect.selectAll("#boosh,#boosh", document)
).to.have.length(1); // two booshes dont make a thing go right
expect(
CSSselect.selectAll("#boosh,.apples,#boosh", document)
).to.have.length(1); // two booshes and an apple dont make a thing go right
},
});
"byId sub-queries within context"() {
it("byId sub-queries within context", () => {
expect(
CSSselect.selectAll(
"#booshTest",
@ -123,17 +133,17 @@ module.exports = {
CSSselect.selectAll("#lonelyBoosh", document)
).length
).to.not.be.ok(); // shouldn't find #boosh within #lonelyBoosh (unrelated)
},
},
});
});
"CSS 1": {
"get element by id"() {
describe("CSS 1", () => {
it("get element by id", () => {
const result = CSSselect.selectAll("#boosh", document);
expect(result[0]).to.be.ok(); // found element with id=boosh
expect(CSSselect.selectAll("h1", document)[0]).to.be.ok(); // found 1 h1
},
});
"byId sub-queries"() {
it("byId sub-queries", () => {
expect(
CSSselect.selectAll("#boosh #booshTest", document)
).to.have.length(1); // found "#id #id"
@ -146,9 +156,9 @@ module.exports = {
expect(
CSSselect.selectAll(".a>#booshTest", document)
).to.have.length(1); // found ".class>#id"
},
});
"get elements by class"() {
it("get elements by class", () => {
expect(CSSselect.selectAll("#boosh .a", document)).to.have.length(
2
); // found two elements
@ -161,25 +171,25 @@ module.exports = {
CSSselect.selectAll("#boosh div div", document)[0]
).to.be.ok(); // found a single div
expect(CSSselect.selectAll("a.odd", document)).to.have.length(1); // found single a
},
});
combos() {
it("combos", () => {
expect(
CSSselect.selectAll("#boosh div,#boosh span", document)
).to.have.length(3); // found 2 divs and 1 span
},
});
"class with dashes"() {
it("class with dashes", () => {
expect(
CSSselect.selectAll(".class-with-dashes", document)
).to.have.length(1); // found something
},
});
"should ignore comment nodes"() {
it("should ignore comment nodes", () => {
expect(CSSselect.selectAll("#boosh *", document)).to.have.length(4); // found only 4 elements under #boosh
},
});
"deep messy relationships"() {
it("deep messy relationships", () => {
// these are mostly characterised by a combination of tight relationships and loose relationships
// on the right side of the query it's easy to find matches but they tighten up quickly as you
// go to the left
@ -215,11 +225,11 @@ module.exports = {
document
)
).to.be.empty(); // found no results for another odd query
},
},
});
});
"CSS 2": {
"get elements by attribute"() {
describe("CSS 2", () => {
it("get elements by attribute", () => {
const wanted = CSSselect.selectAll("#boosh div[test]", document)[0];
const expected = DomUtils.getElementById("booshTest", document);
expect(wanted).to.be(expected); // found attribute
@ -232,15 +242,15 @@ module.exports = {
expect(
CSSselect.selectAll('em[nopass~="copyright"]', document)
).to.be.empty(); // found em[nopass~="copyright"]
},
});
"should not throw error by attribute selector"() {
it("should not throw error by attribute selector", () => {
expect(
CSSselect.selectAll('[foo^="bar"]', document)
).to.have.length(1); // found 1 element
},
});
"crazy town"() {
it("crazy town", () => {
const el = DomUtils.getElementById("attr-test3", document);
expect(
CSSselect.selectAll(
@ -248,20 +258,20 @@ module.exports = {
document
)[0]
).to.be(el); // found the right element
},
},
});
});
"attribute selectors": {
describe("attribute selectors", () => {
/* CSS 2 SPEC */
"[attr]"() {
it("[attr]", () => {
const expected = DomUtils.getElementById("attr-test-1", document);
expect(
CSSselect.selectAll("#attributes div[unique-test]", document)[0]
).to.be(expected); // found attribute with [attr]
},
});
"[attr=val]"() {
it("[attr=val]", () => {
const expected = DomUtils.getElementById("attr-test-2", document);
expect(
CSSselect.selectAll(
@ -281,16 +291,16 @@ module.exports = {
document
)[0]
).to.be(expected); // found attribute with =
},
});
"[attr~=val]"() {
it("[attr~=val]", () => {
const expected = DomUtils.getElementById("attr-test-3", document);
expect(
CSSselect.selectAll("#attributes div[test~=three]", document)[0]
).to.be(expected); // found attribute with ~=
},
});
"[attr|=val]"() {
it("[attr|=val]", () => {
const expected = DomUtils.getElementById("attr-test-2", document);
expect(
CSSselect.selectAll(
@ -301,39 +311,39 @@ module.exports = {
expect(
CSSselect.selectAll("#attributes div[test|=two]", document)[0]
).to.be(expected); // found attribute with |=
},
});
"[href=#x] special case"() {
it("[href=#x] special case", () => {
const expected = DomUtils.getElementById("attr-test-4", document);
expect(
CSSselect.selectAll('#attributes a[href="#aname"]', document)[0]
).to.be(expected); // found attribute with href=#x
},
});
/* CSS 3 SPEC */
"[attr^=val]"() {
it("[attr^=val]", () => {
const expected = DomUtils.getElementById("attr-test-2", document);
expect(
CSSselect.selectAll("#attributes div[test^=two]", document)[0]
).to.be(expected); // found attribute with ^=
},
});
"[attr$=val]"() {
it("[attr$=val]", () => {
const expected = DomUtils.getElementById("attr-test-2", document);
expect(
CSSselect.selectAll("#attributes div[test$=foo]", document)[0]
).to.be(expected); // found attribute with $=
},
});
"[attr*=val]"() {
it("[attr*=val]", () => {
const expected = DomUtils.getElementById("attr-test-3", document);
expect(
CSSselect.selectAll("#attributes div[test*=hree]", document)[0]
).to.be(expected); // found attribute with *=
},
});
"direct descendants"() {
it("direct descendants", () => {
expect(
CSSselect.selectAll(
"#direct-descend > .direct-descend",
@ -346,9 +356,9 @@ module.exports = {
document
)
).to.have.length(3); // found three second-level direct descendents
},
});
"sibling elements"() {
it("sibling elements", () => {
expect(
CSSselect.selectAll(
"#sibling-selector ~ .sibling-selector",
@ -414,20 +424,11 @@ module.exports = {
expect(
CSSselect.selectAll(".parent .youngest + .sibling", document)
).to.be.empty(); // found no younger siblings
},
},
/*
'Uniq': {
'duplicates arent found in arrays': function () {
expect(CSSselect.uniq(['a', 'b', 'c', 'd', 'e', 'a', 'b', 'c', 'd', 'e'])).to.have.length(5); //result should be a, b, c, d, e
expect(CSSselect.uniq(['a', 'b', 'c', 'c', 'c'])).to.have.length(3); //result should be a, b, c
}
},
*/
});
});
"element-context queries": {
"relationship-first queries"() {
describe("element-context queries", () => {
it("relationship-first queries", () => {
expect(
CSSselect.selectAll(
"> .direct-descend",
@ -452,15 +453,15 @@ module.exports = {
CSSselect.selectAll(".idless", document)[0]
)
).to.have.length(1); // found one sibling from a root with no id
},
});
// should be able to query on an element that hasn't been inserted into the dom
"detached fragments"() {
it("detached fragments", () => {
expect(CSSselect.selectAll(".a span", frag)).to.have.length(1); // should find child elements of fragment
expect(CSSselect.selectAll("> div p em", frag)).to.have.length(2); // should find child elements of fragment, relationship first
},
});
"byId sub-queries within detached fragment"() {
it("byId sub-queries within detached fragment", () => {
expect(CSSselect.selectAll("#emem", frag)).to.have.length(1); // found "#id" in fragment
expect(CSSselect.selectAll(".d.i #emem", frag)).to.have.length(1); // found ".class.class #id" in fragment
expect(CSSselect.selectAll(".d #oooo #emem", frag)).to.have.length(
@ -475,30 +476,30 @@ module.exports = {
CSSselect.selectAll("#sep", CSSselect.selectAll("#emem", frag))
.length
).to.not.be.ok(); // shouldn't find #sep within #emem (unrelated)
},
});
"exclude self in match"() {
it("exclude self in match", () => {
expect(
CSSselect.selectAll(
".order-matters",
CSSselect.selectAll("#order-matters", document)[0]
)
).to.have.length(4); // should not include self in element-context queries
},
});
// because form's have .length
"forms can be used as contexts"() {
it("forms can be used as contexts", () => {
expect(
CSSselect.selectAll(
"*",
CSSselect.selectAll("form", document)[0]
)
).to.have.length(3); // found 3 elements under &lt;form&gt;
},
},
});
});
tokenizer: {
"should not get weird tokens"() {
describe("tokenizer", () => {
it("should not get weird tokens", () => {
expect(
CSSselect.selectAll('div .tokens[title="one"]', document)[0]
).to.be(DomUtils.getElementById("token-one", document)); // found div .tokens[title="one"]
@ -523,19 +524,19 @@ module.exports = {
document
)[0]
).to.be(DomUtils.getElementById("token-five", document)); // found div .tokens[title="one two three #%"] a[href=foo] div
},
},
});
});
"interesting syntaxes": {
"should parse bad selectors"() {
describe("interesting syntaxes", () => {
it("should parse bad selectors", () => {
expect(
CSSselect.selectAll("#spaced-tokens p em a", document)
.length
).to.be.ok(); // found element with funny tokens
},
},
});
});
"order matters": {
describe("order matters", () => {
// <div id="order-matters">
// <p class="order-matters"></p>
// <a class="order-matters">
@ -543,23 +544,23 @@ module.exports = {
// </a>
// </div>
"the order of elements return matters"() {
function tag(el) {
it("the order of elements return matters", () => {
function tag(el: Element) {
return el.name.toLowerCase();
}
const els = CSSselect.selectAll(
"#order-matters .order-matters",
document
);
) as Element[];
expect(tag(els[0])).to.be("p"); // first element matched is a {p} tag
expect(tag(els[1])).to.be("a"); // first element matched is a {a} tag
expect(tag(els[2])).to.be("em"); // first element matched is a {em} tag
expect(tag(els[3])).to.be("b"); // first element matched is a {b} tag
},
},
});
});
"pseudo-selectors": {
":contains"() {
describe("pseudo-selectors", () => {
it(":contains", () => {
expect(
CSSselect.selectAll("li:contains(humans)", document)
).to.have.length(1); // found by "element:contains(text)"
@ -573,34 +574,34 @@ module.exports = {
expect(
CSSselect.selectAll("ol:contains(humans)", document)
).to.have.length(1); // found by "ancestor:contains(text)"
},
});
":not"() {
it(":not", () => {
expect(
CSSselect.selectAll(".odd:not(div)", document)
).to.have.length(1); // found one .odd :not an &lt;a&gt;
},
});
":first-child"() {
it(":first-child", () => {
expect(
CSSselect.selectAll("#pseudos div:first-child", document)[0]
).to.be(pseudos[0]); // found first child
expect(
CSSselect.selectAll("#pseudos div:first-child", document)
).to.have.length(1); // found only 1
},
});
":last-child"() {
const all = DomUtils.getElementsByTagName("div", pseudos);
it(":last-child", () => {
const all = getElementsByTagName("div", pseudos);
expect(
CSSselect.selectAll("#pseudos div:last-child", document)[0]
).to.be(all[all.length - 1]); // found last child
expect(
CSSselect.selectAll("#pseudos div:last-child", document)
).to.have.length(1); // found only 1
},
});
'ol > li[attr="boosh"]:last-child'() {
it('ol > li[attr="boosh"]:last-child', () => {
const expected = DomUtils.getElementById(
"attr-child-boosh",
document
@ -617,10 +618,10 @@ module.exports = {
document
)[0]
).to.be(expected); // found correct element
},
});
":nth-child(odd|even|x)"() {
const second = DomUtils.getElementsByTagName("div", pseudos)[1];
it(":nth-child(odd|even|x)", () => {
const second = getElementsByTagName("div", pseudos)[1];
expect(
CSSselect.selectAll("#pseudos :nth-child(odd)", document)
).to.have.length(4); // found 4 odd elements
@ -633,11 +634,11 @@ module.exports = {
expect(
CSSselect.selectAll("#pseudos div:nth-child(2)", document)[0]
).to.be(second); // found 2nd nth-child of pseudos
},
});
":nth-child(expr)"() {
const fifth = DomUtils.getElementsByTagName("a", pseudos)[0];
const sixth = DomUtils.getElementsByTagName("div", pseudos)[4];
it(":nth-child(expr)", () => {
const fifth = getElementsByTagName("a", pseudos)[0];
const sixth = getElementsByTagName("div", pseudos)[4];
expect(
CSSselect.selectAll("#pseudos :nth-child(3n+1)", document)
@ -657,10 +658,10 @@ module.exports = {
expect(
CSSselect.selectAll("#pseudos :nth-child(3n)", document)[1]
).to.be(sixth); // second :nth-child(3n) is the sixth child
},
});
":nth-last-child(odd|even|x)"() {
const second = DomUtils.getElementsByTagName("div", pseudos)[1];
it(":nth-last-child(odd|even|x)", () => {
const second = getElementsByTagName("div", pseudos)[1];
expect(
CSSselect.selectAll("#pseudos :nth-last-child(odd)", document)
).to.have.length(4); // found 4 odd elements
@ -682,10 +683,10 @@ module.exports = {
document
)[0]
).to.be(second); // 6th nth-last-child should be 2nd of 7 elements
},
});
":nth-last-child(expr)"() {
const third = DomUtils.getElementsByTagName("div", pseudos)[2];
it(":nth-last-child(expr)", () => {
const third = getElementsByTagName("div", pseudos)[2];
expect(
CSSselect.selectAll("#pseudos :nth-last-child(3n+1)", document)
@ -705,10 +706,10 @@ module.exports = {
document
)[0]
).to.be(third); // first :nth-last-child(3n+2) is the third child
},
});
":nth-of-type(expr)"() {
const a = DomUtils.getElementsByTagName("a", pseudos)[0];
it(":nth-of-type(expr)", () => {
const a = getElementsByTagName("a", pseudos)[0];
expect(
CSSselect.selectAll("#pseudos div:nth-of-type(3n+1)", document)
@ -728,10 +729,10 @@ module.exports = {
expect(
CSSselect.selectAll("#pseudos a:nth-of-type(1)", document)
).to.have.length(1); // found the first a
},
});
":nth-last-of-type(expr)"() {
const second = DomUtils.getElementsByTagName("div", pseudos)[1];
it(":nth-last-of-type(expr)", () => {
const second = getElementsByTagName("div", pseudos)[1];
expect(
CSSselect.selectAll(
@ -751,37 +752,37 @@ module.exports = {
document
)[0]
).to.be(second); // 5th nth-last-of-type should be 2nd of 7 elements
},
});
":first-of-type"() {
it(":first-of-type", () => {
expect(
CSSselect.selectAll("#pseudos a:first-of-type", document)[0]
).to.be(DomUtils.getElementsByTagName("a", pseudos)[0]); // found first a element
).to.be(getElementsByTagName("a", pseudos)[0]); // found first a element
expect(
CSSselect.selectAll("#pseudos a:first-of-type", document)
).to.have.length(1); // found only 1
},
});
":last-of-type"() {
const all = DomUtils.getElementsByTagName("div", pseudos);
it(":last-of-type", () => {
const all = getElementsByTagName("div", pseudos);
expect(
CSSselect.selectAll("#pseudos div:last-of-type", document)[0]
).to.be(all[all.length - 1]); // found last div element
expect(
CSSselect.selectAll("#pseudos div:last-of-type", document)
).to.have.length(1); // found only 1
},
});
":only-of-type"() {
it(":only-of-type", () => {
expect(
CSSselect.selectAll("#pseudos a:only-of-type", document)[0]
).to.be(DomUtils.getElementsByTagName("a", pseudos)[0]); // found the only a element
).to.be(getElementsByTagName("a", pseudos)[0]); // found the only a element
expect(
CSSselect.selectAll("#pseudos a:first-of-type", document)
).to.have.length(1); // found only 1
},
});
":target"() {
it(":target", () => {
location.hash = "";
expect(
CSSselect.selectAll("#pseudos:target", document)
@ -791,43 +792,18 @@ module.exports = {
CSSselect.selectAll("#pseudos:target", document)
).to.have.length(1); // now #pseudos is the target
location.hash = "";
},
});
"custom pseudos"() {
it("custom pseudos", () => {
// :humanoid implemented just for testing purposes
expect(CSSselect.selectAll(":humanoid", document)).to.have.length(
2
); // selected using custom pseudo
},
},
/*
'argument types': {
'should be able to pass in nodes as arguments': function () {
var el = DomUtils.getElementById('boosh', document);
expect(CSSselect.selectAll(el)[0]).to.be(el); //CSSselect.selectAll(el)[0] == el
expect(CSSselect.selectAll(el, 'body')[0]).to.be(el); //CSSselect.selectAll(el, 'body')[0] == el
expect(CSSselect.selectAll(el, document)[0]).to.be(el); //CSSselect.selectAll(el, document)[0] == el
expect(CSSselect.selectAll(window)[0]).to.be(window); //CSSselect.selectAll(window)[0] == window
expect(CSSselect.selectAll(document)[0]).to.be(document); //CSSselect.selectAll(document)[0] == document
},
'should be able to pass in an array of results as arguments': function () {
var el = DomUtils.getElementById('boosh', document);
var result = CSSselect.selectAll([CSSselect.selectAll('#boosh', document), CSSselect.selectAll(document), CSSselect.selectAll(window)]);
expect(result).to.have.length(3); //3 elements in the combined set
expect(result[0]).to.be(el); //result[0] == el
expect(result[1]).to.be(document); //result[0] == document
expect(result[2]).to.be(window); //result[0] == window
expect(CSSselect.selectAll([CSSselect.selectAll('#pseudos div.odd', document), CSSselect.selectAll('#pseudos div.even', document)])).to.have.length(6); //found all the odd and even divs
}
},
*/
});
});
"is()": {
"simple selectors"() {
describe("is()", () => {
it("simple selectors", () => {
expect(CSSselect.is(el, "li")).to.be.ok(); // tag
expect(CSSselect.is(el, "*")).to.be.ok(); // wildcard
expect(CSSselect.is(el, "#attr-child-boosh")).to.be.ok(); // #id
@ -837,16 +813,18 @@ module.exports = {
expect(CSSselect.is(el, "#foo")).to.not.be.ok(); // wrong #id
expect(CSSselect.is(el, "[foo]")).to.not.be.ok(); // wrong [attr]
expect(CSSselect.is(el, "[attr=foo]")).to.not.be.ok(); // wrong [attr=val]
},
"selector sequences"() {
});
it("selector sequences", () => {
expect(
CSSselect.is(el, "li#attr-child-boosh[attr=boosh]")
).to.be.ok(); // tag#id[attr=val]
expect(
CSSselect.is(el, "div#attr-child-boosh[attr=boosh]")
).to.not.be.ok(); // wrong tag#id[attr=val]
},
"selector sequences combinators"() {
});
it("selector sequences combinators", () => {
expect(CSSselect.is(el, "ol li")).to.be.ok(); // tag tag
expect(CSSselect.is(el, "ol>li")).to.be.ok(); // tag>tag
expect(CSSselect.is(el, "ol>li+li")).to.be.ok(); // tab>tag+tag
@ -865,8 +843,9 @@ module.exports = {
"div#fixtures>div a"
)
).to.be.ok(); // tag#id>tag tag where ambiguous middle tag requires backtracking
},
pseudos() {
});
it("pseudos", () => {
// TODO: more tests!
expect(CSSselect.is(el, "li:contains(hello)")).to.be.ok(); // matching :contains(text)
expect(CSSselect.is(el, "li:contains(human)")).to.not.be.ok(); // non-matching :contains(text)
@ -882,37 +861,44 @@ module.exports = {
":humanoid"
)
).to.not.be.ok(); // non-matching custom pseudo
},
context() {
});
it("context", () => {
expect(
CSSselect.is(el, "li#attr-child-boosh[attr=boosh]", {
context: CSSselect.selectAll("#list", document)[0],
context: CSSselect.selectAll(
"#list",
document
)[0] as Element,
})
).to.be.ok(); // context
expect(
CSSselect.is(el, "ol#list li#attr-child-boosh[attr=boosh]", {
context: CSSselect.selectAll("#boosh", document)[0],
context: CSSselect.selectAll(
"#boosh",
document
)[0] as Element,
})
).to.not.be.ok(); // wrong context
},
},
});
});
"selecting elements in other documents": {
"get element by id"() {
describe("selecting elements in other documents", () => {
it("get element by id", () => {
const result = CSSselect.selectAll("#hsoob", doc);
expect(result[0]).to.be.ok(); // found element with id=hsoob
},
});
"get elements by class"() {
it("get elements by class", () => {
expect(CSSselect.selectAll("#hsoob .a", doc)).to.have.length(2); // found two elements
expect(CSSselect.selectAll("#hsoob div.a", doc)[0]).to.be.ok(); // found one element
expect(CSSselect.selectAll("#hsoob div", doc)).to.have.length(2); // found two {div} elements
expect(CSSselect.selectAll("#hsoob span", doc)[0]).to.be.ok(); // found one {span} element
expect(CSSselect.selectAll("#hsoob div div", doc)[0]).to.be.ok(); // found a single div
expect(CSSselect.selectAll("p.odd", doc)).to.have.length(1); // found single br
},
});
"complex selectors"() {
it("complex selectors", () => {
expect(CSSselect.selectAll(".d ~ .sib", doc)).to.have.length(2); // found one ~ sibling
expect(CSSselect.selectAll(".a .d + .sib", doc)).to.have.length(1); // found 2 + siblings
expect(
@ -921,9 +907,9 @@ module.exports = {
expect(
CSSselect.selectAll('.a .d ~ .sib[test="f g"]', doc)
).to.have.length(1); // found 1 ~ sibling with test attribute
},
});
"byId sub-queries"() {
it("byId sub-queries", () => {
expect(CSSselect.selectAll("#hsoob #spanny", doc)).to.have.length(
1
); // found "#id #id" in frame
@ -932,9 +918,9 @@ module.exports = {
CSSselect.selectAll(".a #booshTest #spanny", doc)
).to.have.length(1); // found ".class #id #id" in frame
expect(CSSselect.selectAll("> #hsoob", doc)).to.have.length(1); // found "> #id" in frame
},
});
"byId sub-queries within sub-context"() {
it("byId sub-queries within sub-context", () => {
expect(
CSSselect.selectAll(
"#spanny",
@ -971,6 +957,6 @@ module.exports = {
CSSselect.selectAll("#lonelyHsoob", doc)
).length
).to.not.be.ok(); // shouldn't find #booshTest within #lonelyHsoob (unrelated)
},
},
};
});
});
});

File diff suppressed because it is too large Load Diff

@ -1,250 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en" dir="ltr" id="html">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Sizzle Test Suite</title>
<link rel="Stylesheet" media="screen" href="../bower_components/qunit/qunit/qunit.css" />
<script type="text/javascript" src="../bower_components/qunit/qunit/qunit.js"></script>
<script type="text/javascript" src="data/testinit.js"></script>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="../dist/sizzle.js"></script>
<script type="text/javascript" src="unit/selector.js"></script>
<script type="text/javascript" src="unit/utilities.js"></script>
<script type="text/javascript" src="unit/extending.js"></script>
</head>
<body id="body">
<div id="qunit">
<h1 id="qunit-header">jQuery Test Suite</h1>
<h2 id="qunit-banner"></h2>
<div id="qunit-testrunner-toolbar"></div>
<h2 id="qunit-userAgent"></h2>
</div>
<!-- Test HTML -->
<dl id="dl" style="position:absolute;top:-32767px;left:-32767px;width:1px">
<div id="qunit-fixture">
<p id="firstp">See <a id="simon1" href="http://simon.incutio.com/archive/2003/03/25/#getElementsBySelector" rel="bookmark">this blog entry</a> for more information.</p>
<p id="ap">
Here are some [links] in a normal paragraph: <a id="google" href="http://www.google.com/" title="Google!">Google</a>,
<a id="groups" href="http://groups.google.com/" class="GROUPS">Google Groups (Link)</a>.
This link has <code id="code1"><a href="http://smin" id="anchor1">class="blog"</a></code>:
<a href="http://diveintomark.org/" class="blog" hreflang="en" id="mark">diveintomark</a>
</p>
<div id="foo">
<p id="sndp">Everything inside the red border is inside a div with <code>id="foo"</code>.</p>
<p lang="en" id="en">This is a normal link: <a id="yahoo" href="http://www.yahoo.com/" class="blogTest">Yahoo</a></p>
<p id="sap">This link has <code><a href="#2" id="anchor2">class="blog"</a></code>: <a href="http://simon.incutio.com/" class="blog link" id="simon">Simon Willison's Weblog</a></p>
</div>
<div id="nothiddendiv" style="height:1px;background:white;" class="nothiddendiv">
<div id="nothiddendivchild"></div>
</div>
<span id="name+value"></span>
<p id="first">Try them out:</p>
<ul id="firstUL"></ul>
<ol id="empty"><!-- comment --></ol>
<form id="form" action="formaction">
<label for="action" id="label-for">Action:</label>
<input type="text" name="action" value="Test" id="text1" maxlength="30"/>
<input type="text" name="text2" value="Test" id="text2" disabled="disabled"/>
<input type="radio" name="radio1" id="radio1" value="on"/>
<input type="radio" name="radio2" id="radio2" checked="checked"/>
<input type="checkbox" name="check" id="check1" checked="checked"/>
<input type="checkbox" id="check2" value="on"/>
<input type="hidden" name="hidden" id="hidden1"/>
<input type="text" style="display:none;" name="foo[bar]" id="hidden2"/>
<input type="text" id="name" name="name" value="name" />
<input type="search" id="search" name="search" value="search" />
<button id="button" name="button" type="button">Button</button>
<textarea id="area1" maxlength="30">foobar</textarea>
<select name="select1" id="select1">
<option id="option1a" class="emptyopt" value="">Nothing</option>
<option id="option1b" value="1">1</option>
<option id="option1c" value="2">2</option>
<option id="option1d" value="3">3</option>
</select>
<select name="select2" id="select2">
<option id="option2a" class="emptyopt" value="">Nothing</option>
<option id="option2b" value="1">1</option>
<option id="option2c" value="2">2</option>
<option id="option2d" selected="selected" value="3">3</option>
</select>
<select name="select3" id="select3" multiple="multiple">
<option id="option3a" class="emptyopt" value="">Nothing</option>
<option id="option3b" selected="selected" value="1">1</option>
<option id="option3c" selected="selected" value="2">2</option>
<option id="option3d" value="3">3</option>
<option id="option3e">no value</option>
</select>
<select name="select4" id="select4" multiple="multiple">
<optgroup disabled="disabled">
<option id="option4a" class="emptyopt" value="">Nothing</option>
<option id="option4b" disabled="disabled" selected="selected" value="1">1</option>
<option id="option4c" selected="selected" value="2">2</option>
</optgroup>
<option selected="selected" disabled="disabled" id="option4d" value="3">3</option>
<option id="option4e">no value</option>
</select>
<select name="select5" id="select5">
<option id="option5a" value="3">1</option>
<option id="option5b" value="2">2</option>
<option id="option5c" value="1">3</option>
</select>
<object id="object1" codebase="stupid">
<param name="p1" value="x1" />
<param name="p2" value="x2" />
</object>
<span id="台北Táiběi"></span>
<span id="台北" lang="中文"></span>
<span id="utf8class1" class="台北Táiběi 台北"></span>
<span id="utf8class2" class="台北"></span>
<span id="foo:bar" class="foo:bar"><span id="foo_descendent"></span></span>
<span id="test.foo[5]bar" class="test.foo[5]bar"></span>
<foo_bar id="foobar">test element</foo_bar>
</form>
<b id="floatTest">Float test.</b>
<iframe id="iframe" name="iframe"></iframe>
<form id="lengthtest">
<input type="text" id="length" name="test"/>
<input type="text" id="idTest" name="id"/>
</form>
<table id="table"></table>
<form id="name-tests">
<!-- Inputs with a grouped name attribute. -->
<input name="types[]" id="types_all" type="checkbox" value="all" />
<input name="types[]" id="types_anime" type="checkbox" value="anime" />
<input name="types[]" id="types_movie" type="checkbox" value="movie" />
</form>
<form id="testForm" action="#" method="get">
<textarea name="T3" rows="2" cols="15">?
Z</textarea>
<input type="hidden" name="H1" value="x" />
<input type="hidden" name="H2" />
<input name="PWD" type="password" value="" />
<input name="T1" type="text" />
<input name="T2" type="text" value="YES" readonly="readonly" />
<input type="checkbox" name="C1" value="1" />
<input type="checkbox" name="C2" />
<input type="radio" name="R1" value="1" />
<input type="radio" name="R1" value="2" />
<input type="text" name="My Name" value="me" />
<input type="reset" name="reset" value="NO" />
<select name="S1">
<option value="abc">ABC</option>
<option value="abc">ABC</option>
<option value="abc">ABC</option>
</select>
<select name="S2" multiple="multiple" size="3">
<option value="abc">ABC</option>
<option value="abc">ABC</option>
<option value="abc">ABC</option>
</select>
<select name="S3">
<option selected="selected">YES</option>
</select>
<select name="S4">
<option value="" selected="selected">NO</option>
</select>
<input type="submit" name="sub1" value="NO" />
<input type="submit" name="sub2" value="NO" />
<input type="image" name="sub3" value="NO" />
<button name="sub4" type="submit" value="NO">NO</button>
<input name="D1" type="text" value="NO" disabled="disabled" />
<input type="checkbox" checked="checked" disabled="disabled" name="D2" value="NO" />
<input type="radio" name="D3" value="NO" checked="checked" disabled="disabled" />
<select name="D4" disabled="disabled">
<option selected="selected" value="NO">NO</option>
</select>
<input id="list-test" type="text" />
<datalist id="datalist">
<option value="option"></option>
</datalist>
</form>
<div id="moretests">
<form>
<div id="checkedtest" style="display:none;">
<input type="radio" name="checkedtestradios" checked="checked"/>
<input type="radio" name="checkedtestradios" value="on"/>
<input type="checkbox" name="checkedtestcheckboxes" checked="checked"/>
<input type="checkbox" name="checkedtestcheckboxes" />
</div>
</form>
<div id="nonnodes"><span>hi</span> there <!-- mon ami --></div>
<div id="t2037">
<div><div class="hidden">hidden</div></div>
</div>
<div id="t6652">
<div></div>
</div>
<div id="t12087">
<input type="hidden" id="el12087" data-comma="0,1"/>
</div>
<div id="no-clone-exception"><object><embed></embed></object></div>
<div id="names-group">
<span id="name-is-example" name="example"></span>
<span id="name-is-div" name="div"></span>
</div>
<script id="script-no-src"></script>
<script id="script-src" src="http://src.test/path"></script>
<div id="id-name-tests">
<a id="tName1ID" name="tName1"><span></span></a>
<a id="tName2ID" name="tName2"><span></span></a>
<div id="tName1"><span id="tName1-span">C</span></div>
</div>
</div>
<div id="tabindex-tests">
<ol id="listWithTabIndex" tabindex="5">
<li id="foodWithNegativeTabIndex" tabindex="-1">Rice</li>
<li id="foodNoTabIndex">Beans</li>
<li>Blinis</li>
<li>Tofu</li>
</ol>
<div id="divWithNoTabIndex">I'm hungry. I should...</div>
<span>...</span><a href="#" id="linkWithNoTabIndex">Eat lots of food</a><span>...</span> |
<span>...</span><a href="#" id="linkWithTabIndex" tabindex="2">Eat a little food</a><span>...</span> |
<span>...</span><a href="#" id="linkWithNegativeTabIndex" tabindex="-1">Eat no food</a><span>...</span>
<span>...</span><a id="linkWithNoHrefWithNoTabIndex">Eat a burger</a><span>...</span>
<span>...</span><a id="linkWithNoHrefWithTabIndex" tabindex="1">Eat some funyuns</a><span>...</span>
<span>...</span><a id="linkWithNoHrefWithNegativeTabIndex" tabindex="-1">Eat some funyuns</a><span>...</span>
</div>
<div id="liveHandlerOrder">
<span id="liveSpan1"><a href="#" id="liveLink1"></a></span>
<span id="liveSpan2"><a href="#" id="liveLink2"></a></span>
</div>
<div id="siblingTest">
<em id="siblingfirst">1</em>
<em id="siblingnext">2</em>
<em id="siblingthird">
<em id="siblingchild">
<em id="siblinggrandchild">
<em id="siblinggreatgrandchild"></em>
</em>
</em>
</em>
<span id="siblingspan"></span>
</div>
</div>
</dl>
<div id="scopeTest">
<label id="scopeTest--child"></label>
</div>
<br id="last"/>
</body>
</html>

@ -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…
Cancel
Save