@markuplint/selector 3.12.0 → 4.0.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/compare-specificity.d.ts +1 -1
- package/lib/compare-specificity.js +1 -5
- package/lib/create-selector.d.ts +1 -1
- package/lib/create-selector.js +9 -13
- package/lib/debug.js +9 -14
- package/lib/extended-selector/aria-pseudo-class.d.ts +1 -1
- package/lib/extended-selector/aria-pseudo-class.js +6 -10
- package/lib/extended-selector/aria-role-pseudo-class.d.ts +1 -1
- package/lib/extended-selector/aria-role-pseudo-class.js +7 -13
- package/lib/extended-selector/content-model-pseudo-class.d.ts +1 -1
- package/lib/extended-selector/content-model-pseudo-class.js +5 -9
- package/lib/index.d.ts +5 -5
- package/lib/index.js +5 -13
- package/lib/invalid-selector-error.js +2 -6
- package/lib/is.js +3 -9
- package/lib/match-selector.d.ts +1 -1
- package/lib/match-selector.js +17 -21
- package/lib/regex-selector-matches.js +1 -5
- package/lib/selector.d.ts +1 -1
- package/lib/selector.js +55 -65
- package/lib/types.js +1 -2
- package/package.json +11 -6
- package/test/create-selector.spec.js +0 -44
- package/test/match-selector.spec.js +0 -342
- package/test/regex-selector-matches.spec.js +0 -16
- package/test/selector.spec.js +0 -284
package/lib/selector.js
CHANGED
|
@@ -1,27 +1,24 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
var _Selector_ruleset, _Ruleset_selectorGroup, _StructuredSelector_edge, _StructuredSelector_selector, _SelectorTarget_combinedFrom, _SelectorTarget_extended, _SelectorTarget_isAdded;
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
const resLog = debug_1.log.extend('result');
|
|
14
|
-
class Selector {
|
|
2
|
+
import { __classPrivateFieldGet, __classPrivateFieldSet } from "tslib";
|
|
3
|
+
import { resolveNamespace } from '@markuplint/ml-spec';
|
|
4
|
+
import parser from 'postcss-selector-parser';
|
|
5
|
+
import { compareSpecificity } from './compare-specificity.js';
|
|
6
|
+
import { log as coreLog } from './debug.js';
|
|
7
|
+
import { InvalidSelectorError } from './invalid-selector-error.js';
|
|
8
|
+
import { isElement, isNonDocumentTypeChildNode, isPureHTMLElement } from './is.js';
|
|
9
|
+
const selLog = coreLog.extend('selector');
|
|
10
|
+
const resLog = coreLog.extend('result');
|
|
11
|
+
export class Selector {
|
|
15
12
|
constructor(selector, extended = {}) {
|
|
16
13
|
_Selector_ruleset.set(this, void 0);
|
|
17
|
-
|
|
14
|
+
__classPrivateFieldSet(this, _Selector_ruleset, Ruleset.parse(selector, extended), "f");
|
|
18
15
|
}
|
|
19
16
|
match(
|
|
20
17
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
21
18
|
el,
|
|
22
19
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
23
20
|
scope) {
|
|
24
|
-
scope =
|
|
21
|
+
scope = isElement(el) ? el : null;
|
|
25
22
|
const results = this.search(el, scope);
|
|
26
23
|
for (const result of results) {
|
|
27
24
|
if (result.matched) {
|
|
@@ -35,38 +32,36 @@ class Selector {
|
|
|
35
32
|
el,
|
|
36
33
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
37
34
|
scope) {
|
|
38
|
-
scope =
|
|
39
|
-
return
|
|
35
|
+
scope = isElement(el) ? el : null;
|
|
36
|
+
return __classPrivateFieldGet(this, _Selector_ruleset, "f").match(el, scope);
|
|
40
37
|
}
|
|
41
38
|
}
|
|
42
|
-
exports.Selector = Selector;
|
|
43
39
|
_Selector_ruleset = new WeakMap();
|
|
44
40
|
class Ruleset {
|
|
45
41
|
static parse(selector, extended) {
|
|
46
42
|
const selectors = [];
|
|
47
43
|
try {
|
|
48
|
-
(
|
|
44
|
+
parser(root => {
|
|
49
45
|
selectors.push(...root.nodes);
|
|
50
46
|
}).processSync(selector);
|
|
51
47
|
}
|
|
52
48
|
catch (e) {
|
|
53
49
|
if (e instanceof Error) {
|
|
54
|
-
throw new
|
|
50
|
+
throw new InvalidSelectorError(selector);
|
|
55
51
|
}
|
|
56
52
|
throw e;
|
|
57
53
|
}
|
|
58
54
|
return new Ruleset(selectors, extended, 0);
|
|
59
55
|
}
|
|
60
56
|
constructor(selectors, extended, depth) {
|
|
61
|
-
var _a, _b, _c;
|
|
62
57
|
_Ruleset_selectorGroup.set(this, []);
|
|
63
|
-
|
|
64
|
-
const head =
|
|
65
|
-
this.headCombinator =
|
|
58
|
+
__classPrivateFieldGet(this, _Ruleset_selectorGroup, "f").push(...selectors.map(selector => new StructuredSelector(selector, depth, extended)));
|
|
59
|
+
const head = __classPrivateFieldGet(this, _Ruleset_selectorGroup, "f")[0];
|
|
60
|
+
this.headCombinator = head?.headCombinator ?? null;
|
|
66
61
|
if (this.headCombinator) {
|
|
67
62
|
if (depth <= 0) {
|
|
68
|
-
if (
|
|
69
|
-
throw new
|
|
63
|
+
if (__classPrivateFieldGet(this, _Ruleset_selectorGroup, "f")[0]?.selector) {
|
|
64
|
+
throw new InvalidSelectorError(__classPrivateFieldGet(this, _Ruleset_selectorGroup, "f")[0]?.selector);
|
|
70
65
|
}
|
|
71
66
|
throw new Error('Combinated selector depth is not expected');
|
|
72
67
|
}
|
|
@@ -77,11 +72,11 @@ class Ruleset {
|
|
|
77
72
|
el,
|
|
78
73
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
79
74
|
scope) {
|
|
80
|
-
(
|
|
81
|
-
return
|
|
75
|
+
coreLog('<%s> (%s)', isElement(el) ? el.localName : el.nodeName, scope ? (isElement(scope) ? scope.localName : scope.nodeName) : null);
|
|
76
|
+
return __classPrivateFieldGet(this, _Ruleset_selectorGroup, "f").map(selector => {
|
|
82
77
|
selLog('"%s"', selector.selector);
|
|
83
78
|
const res = selector.match(el, scope);
|
|
84
|
-
resLog('%s "%s" => %o',
|
|
79
|
+
resLog('%s "%s" => %o', isElement(el) ? el.localName : el.nodeName, selector.selector, res);
|
|
85
80
|
return res;
|
|
86
81
|
});
|
|
87
82
|
}
|
|
@@ -89,23 +84,23 @@ class Ruleset {
|
|
|
89
84
|
_Ruleset_selectorGroup = new WeakMap();
|
|
90
85
|
class StructuredSelector {
|
|
91
86
|
constructor(selector, depth, extended) {
|
|
92
|
-
var _a, _b;
|
|
93
87
|
_StructuredSelector_edge.set(this, void 0);
|
|
94
88
|
_StructuredSelector_selector.set(this, void 0);
|
|
95
|
-
|
|
96
|
-
|
|
89
|
+
__classPrivateFieldSet(this, _StructuredSelector_selector, selector, "f");
|
|
90
|
+
__classPrivateFieldSet(this, _StructuredSelector_edge, new SelectorTarget(extended, depth), "f");
|
|
97
91
|
this.headCombinator =
|
|
98
|
-
|
|
99
|
-
const nodes =
|
|
92
|
+
__classPrivateFieldGet(this, _StructuredSelector_selector, "f").nodes[0]?.type === 'combinator' ? __classPrivateFieldGet(this, _StructuredSelector_selector, "f").nodes[0].value ?? null : null;
|
|
93
|
+
const nodes = __classPrivateFieldGet(this, _StructuredSelector_selector, "f").nodes.slice();
|
|
100
94
|
if (0 < depth && this.headCombinator) {
|
|
101
|
-
|
|
95
|
+
// eslint-disable-next-line import/no-named-as-default-member
|
|
96
|
+
nodes.unshift(parser.pseudo({ value: ':scope' }));
|
|
102
97
|
}
|
|
103
98
|
nodes.forEach(node => {
|
|
104
99
|
switch (node.type) {
|
|
105
100
|
case 'combinator': {
|
|
106
101
|
const combinedTarget = new SelectorTarget(extended, depth);
|
|
107
|
-
combinedTarget.from(
|
|
108
|
-
|
|
102
|
+
combinedTarget.from(__classPrivateFieldGet(this, _StructuredSelector_edge, "f"), node);
|
|
103
|
+
__classPrivateFieldSet(this, _StructuredSelector_edge, combinedTarget, "f");
|
|
109
104
|
break;
|
|
110
105
|
}
|
|
111
106
|
case 'root':
|
|
@@ -119,20 +114,20 @@ class StructuredSelector {
|
|
|
119
114
|
throw new Error(`Unsupported comment in selector: ${selector.toString()}`);
|
|
120
115
|
}
|
|
121
116
|
default: {
|
|
122
|
-
|
|
117
|
+
__classPrivateFieldGet(this, _StructuredSelector_edge, "f").add(node);
|
|
123
118
|
}
|
|
124
119
|
}
|
|
125
120
|
});
|
|
126
121
|
}
|
|
127
122
|
get selector() {
|
|
128
|
-
return
|
|
123
|
+
return __classPrivateFieldGet(this, _StructuredSelector_selector, "f").nodes.join('');
|
|
129
124
|
}
|
|
130
125
|
match(
|
|
131
126
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
132
127
|
el,
|
|
133
128
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
134
129
|
scope) {
|
|
135
|
-
return
|
|
130
|
+
return __classPrivateFieldGet(this, _StructuredSelector_edge, "f").match(el, scope, 0);
|
|
136
131
|
}
|
|
137
132
|
}
|
|
138
133
|
_StructuredSelector_edge = new WeakMap(), _StructuredSelector_selector = new WeakMap();
|
|
@@ -146,11 +141,11 @@ class SelectorTarget {
|
|
|
146
141
|
_SelectorTarget_isAdded.set(this, false);
|
|
147
142
|
this.pseudo = [];
|
|
148
143
|
this.tag = null;
|
|
149
|
-
|
|
144
|
+
__classPrivateFieldSet(this, _SelectorTarget_extended, extended, "f");
|
|
150
145
|
this.depth = depth;
|
|
151
146
|
}
|
|
152
147
|
add(selector) {
|
|
153
|
-
|
|
148
|
+
__classPrivateFieldSet(this, _SelectorTarget_isAdded, true, "f");
|
|
154
149
|
switch (selector.type) {
|
|
155
150
|
case 'tag':
|
|
156
151
|
case 'universal': {
|
|
@@ -176,31 +171,29 @@ class SelectorTarget {
|
|
|
176
171
|
}
|
|
177
172
|
}
|
|
178
173
|
from(target, combinator) {
|
|
179
|
-
|
|
174
|
+
__classPrivateFieldSet(this, _SelectorTarget_combinedFrom, { target, combinator }, "f");
|
|
180
175
|
}
|
|
181
176
|
match(
|
|
182
177
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
183
178
|
el,
|
|
184
179
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
185
180
|
scope, count) {
|
|
186
|
-
var _a, _b, _c;
|
|
187
181
|
const result = this._match(el, scope, count);
|
|
188
182
|
if (selLog.enabled) {
|
|
189
183
|
const nodeName = el.nodeName;
|
|
190
|
-
const selector =
|
|
184
|
+
const selector = __classPrivateFieldGet(this, _SelectorTarget_combinedFrom, "f")?.target.toString() ?? this.toString();
|
|
191
185
|
const combinator = result.combinator ? ` ${result.combinator}` : '';
|
|
192
186
|
selLog('The %s element by "%s" => %s (%d)', nodeName, `${selector}${combinator}`, result.matched, count);
|
|
193
187
|
if (selector === ':scope') {
|
|
194
|
-
selLog(`† Scope is the ${
|
|
188
|
+
selLog(`† Scope is the ${scope?.nodeName ?? null}`);
|
|
195
189
|
}
|
|
196
190
|
}
|
|
197
191
|
delete result.combinator;
|
|
198
192
|
return result;
|
|
199
193
|
}
|
|
200
194
|
toString() {
|
|
201
|
-
var _a, _b;
|
|
202
195
|
return [
|
|
203
|
-
|
|
196
|
+
this.tag?.toString() ?? '',
|
|
204
197
|
this.id.map(id => `#${id.value}`).join(''),
|
|
205
198
|
this.class.map(c => `.${c.value}`).join(''),
|
|
206
199
|
this.attr.map(attr => `[${attr.toString()}]`).join(''),
|
|
@@ -216,13 +209,13 @@ class SelectorTarget {
|
|
|
216
209
|
if (!unitCheck.matched) {
|
|
217
210
|
return unitCheck;
|
|
218
211
|
}
|
|
219
|
-
if (!
|
|
212
|
+
if (!__classPrivateFieldGet(this, _SelectorTarget_combinedFrom, "f")) {
|
|
220
213
|
return unitCheck;
|
|
221
214
|
}
|
|
222
|
-
if (!
|
|
215
|
+
if (!isNonDocumentTypeChildNode(el)) {
|
|
223
216
|
return unitCheck;
|
|
224
217
|
}
|
|
225
|
-
const { target, combinator } =
|
|
218
|
+
const { target, combinator } = __classPrivateFieldGet(this, _SelectorTarget_combinedFrom, "f");
|
|
226
219
|
switch (combinator.value) {
|
|
227
220
|
// Descendant combinator
|
|
228
221
|
case ' ': {
|
|
@@ -418,7 +411,7 @@ class SelectorTarget {
|
|
|
418
411
|
throw new Error('Unsupported column combinator yet. If you want it, please request it as the issue (https://github.com/markuplint/markuplint/issues/new).');
|
|
419
412
|
}
|
|
420
413
|
default: {
|
|
421
|
-
throw new Error(`Unsupported ${
|
|
414
|
+
throw new Error(`Unsupported ${__classPrivateFieldGet(this, _SelectorTarget_combinedFrom, "f").combinator.value} combinator in selector`);
|
|
422
415
|
}
|
|
423
416
|
}
|
|
424
417
|
}
|
|
@@ -427,9 +420,8 @@ class SelectorTarget {
|
|
|
427
420
|
el,
|
|
428
421
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
429
422
|
scope) {
|
|
430
|
-
var _a;
|
|
431
423
|
const specificity = [0, 0, 0];
|
|
432
|
-
if (!
|
|
424
|
+
if (!isElement(el)) {
|
|
433
425
|
return {
|
|
434
426
|
specificity,
|
|
435
427
|
matched: false,
|
|
@@ -456,12 +448,12 @@ class SelectorTarget {
|
|
|
456
448
|
break;
|
|
457
449
|
}
|
|
458
450
|
default: {
|
|
459
|
-
throw new
|
|
451
|
+
throw new InvalidSelectorError(`The ${namespace} namespace is not supported`);
|
|
460
452
|
}
|
|
461
453
|
}
|
|
462
454
|
}
|
|
463
455
|
let matched = true;
|
|
464
|
-
if (!
|
|
456
|
+
if (!__classPrivateFieldGet(this, _SelectorTarget_isAdded, "f") && !isScope(el, scope)) {
|
|
465
457
|
matched = false;
|
|
466
458
|
}
|
|
467
459
|
if (!this.id.every(id => id.value === el.id)) {
|
|
@@ -477,7 +469,7 @@ class SelectorTarget {
|
|
|
477
469
|
}
|
|
478
470
|
specificity[1] += this.attr.length;
|
|
479
471
|
for (const pseudo of this.pseudo) {
|
|
480
|
-
const pseudoRes = pseudoMatch(pseudo, el, scope,
|
|
472
|
+
const pseudoRes = pseudoMatch(pseudo, el, scope, __classPrivateFieldGet(this, _SelectorTarget_extended, "f"), this.depth);
|
|
481
473
|
specificity[0] += pseudoRes.specificity[0];
|
|
482
474
|
specificity[1] += pseudoRes.specificity[1];
|
|
483
475
|
specificity[2] += pseudoRes.specificity[2];
|
|
@@ -485,7 +477,7 @@ class SelectorTarget {
|
|
|
485
477
|
has.push(...pseudoRes.has);
|
|
486
478
|
}
|
|
487
479
|
else {
|
|
488
|
-
not.push(...(
|
|
480
|
+
not.push(...(pseudoRes.not ?? []));
|
|
489
481
|
matched = false;
|
|
490
482
|
}
|
|
491
483
|
}
|
|
@@ -493,7 +485,7 @@ class SelectorTarget {
|
|
|
493
485
|
specificity[2] += 1;
|
|
494
486
|
let a = this.tag.value;
|
|
495
487
|
let b = el.localName;
|
|
496
|
-
if (
|
|
488
|
+
if (isPureHTMLElement(el)) {
|
|
497
489
|
a = a.toLowerCase();
|
|
498
490
|
b = b.toLowerCase();
|
|
499
491
|
}
|
|
@@ -525,7 +517,7 @@ el) {
|
|
|
525
517
|
return false;
|
|
526
518
|
}
|
|
527
519
|
if (attr.namespace != null && attr.namespace !== true && attr.namespace !== '*') {
|
|
528
|
-
const ns =
|
|
520
|
+
const ns = resolveNamespace(attrOfEl.localName, attrOfEl.namespaceURI);
|
|
529
521
|
if (attr.namespace !== ns.namespace) {
|
|
530
522
|
return false;
|
|
531
523
|
}
|
|
@@ -795,8 +787,7 @@ function isScope(
|
|
|
795
787
|
el,
|
|
796
788
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
797
789
|
scope) {
|
|
798
|
-
|
|
799
|
-
return (_a = el === scope) !== null && _a !== void 0 ? _a : el.parentNode === null;
|
|
790
|
+
return el === scope ?? el.parentNode === null;
|
|
800
791
|
}
|
|
801
792
|
function getDescendants(
|
|
802
793
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
@@ -811,8 +802,7 @@ el, includeSelf = false) {
|
|
|
811
802
|
function getSiblings(
|
|
812
803
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
813
804
|
el) {
|
|
814
|
-
|
|
815
|
-
return Array.from((_b = (_a = el.parentElement) === null || _a === void 0 ? void 0 : _a.children) !== null && _b !== void 0 ? _b : []);
|
|
805
|
+
return Array.from(el.parentElement?.children ?? []);
|
|
816
806
|
}
|
|
817
807
|
function getSpecificity(
|
|
818
808
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
@@ -820,7 +810,7 @@ results) {
|
|
|
820
810
|
let specificity = undefined;
|
|
821
811
|
for (const result of results) {
|
|
822
812
|
if (specificity) {
|
|
823
|
-
const order =
|
|
813
|
+
const order = compareSpecificity(specificity, result.specificity);
|
|
824
814
|
if (order === -1) {
|
|
825
815
|
specificity = result.specificity;
|
|
826
816
|
}
|
package/lib/types.js
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@markuplint/selector",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0-alpha.2",
|
|
4
4
|
"description": "Extended W3C Selectors matcher",
|
|
5
5
|
"repository": "git@github.com:markuplint/markuplint.git",
|
|
6
6
|
"author": "Yusuke Hirao <yusukehirao@me.com>",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"private": false,
|
|
9
|
-
"
|
|
9
|
+
"type": "module",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"import": "./lib/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
10
15
|
"types": "lib/index.d.ts",
|
|
11
16
|
"publishConfig": {
|
|
12
17
|
"access": "public"
|
|
@@ -20,16 +25,16 @@
|
|
|
20
25
|
"clean": "tsc --build --clean"
|
|
21
26
|
},
|
|
22
27
|
"dependencies": {
|
|
23
|
-
"@markuplint/ml-spec": "
|
|
24
|
-
"@types/debug": "^4.1.
|
|
28
|
+
"@markuplint/ml-spec": "4.0.0-alpha.2",
|
|
29
|
+
"@types/debug": "^4.1.9",
|
|
25
30
|
"debug": "^4.3.4",
|
|
26
31
|
"postcss-selector-parser": "^6.0.13",
|
|
27
32
|
"tslib": "^2.6.2",
|
|
28
33
|
"type-fest": "^4.3.1"
|
|
29
34
|
},
|
|
30
35
|
"devDependencies": {
|
|
31
|
-
"@types/jsdom": "21.1.
|
|
36
|
+
"@types/jsdom": "21.1.3",
|
|
32
37
|
"jsdom": "22.1.0"
|
|
33
38
|
},
|
|
34
|
-
"gitHead": "
|
|
39
|
+
"gitHead": "51ad52c760435641f9bff8685707d8178b0787eb"
|
|
35
40
|
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
const specs = require('@markuplint/html-spec');
|
|
2
|
-
const { createJSDOMElement } = require('@markuplint/test-tools');
|
|
3
|
-
|
|
4
|
-
const { createSelector } = require('../lib/create-selector');
|
|
5
|
-
|
|
6
|
-
function c(selector, html) {
|
|
7
|
-
return createSelector(selector, specs).match(createJSDOMElement(html));
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
describe('Extended Selector', () => {
|
|
11
|
-
it(':aria', () => {
|
|
12
|
-
expect(c(':aria(has name)', '<button>foo</button>')).toBeTruthy();
|
|
13
|
-
expect(c(':aria(has name|1.1)', '<button>foo</button>')).toBeTruthy();
|
|
14
|
-
expect(c(':aria(has name|1.2)', '<button>foo</button>')).toBeTruthy();
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it(':role', () => {
|
|
18
|
-
expect(c(':role(button)', '<button>foo</button>')).toBeTruthy();
|
|
19
|
-
expect(c(':role(button|1.1)', '<button>foo</button>')).toBeTruthy();
|
|
20
|
-
expect(c(':role(button|1.2)', '<button>foo</button>')).toBeTruthy();
|
|
21
|
-
expect(c(':role(button|1.2)', '<div role="button">foo</div>')).toBeTruthy();
|
|
22
|
-
expect(c(':role(button|1.2)', '<div>foo</div>')).toBeFalsy();
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it(':model', () => {
|
|
26
|
-
expect(c(':model(flow)', '<a></a>')).toBeTruthy();
|
|
27
|
-
expect(c(':model(interactive)', '<a></a>')).toBeFalsy();
|
|
28
|
-
expect(c(':model(interactive)', '<a href="path/to"></a>')).toBeTruthy();
|
|
29
|
-
expect(c(':not(:model(interactive))', '<a href="path/to"></a>')).toBeFalsy();
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it('The address element', () => {
|
|
33
|
-
const contentModel =
|
|
34
|
-
':model(flow):not(address, :model(heading), :model(sectioning), header, footer, :has(address, :model(heading), :model(sectioning), header, footer))';
|
|
35
|
-
expect(c(contentModel, '<a></a>')).toBeTruthy();
|
|
36
|
-
expect(c(contentModel, '<h1></h1>')).toBeFalsy();
|
|
37
|
-
expect(c(contentModel, '<address></address>')).toBeFalsy();
|
|
38
|
-
expect(c(contentModel, '<header></header>')).toBeFalsy();
|
|
39
|
-
expect(c(contentModel, '<div><a></a></div>')).toBeTruthy();
|
|
40
|
-
expect(c(contentModel, '<div><h1></h1></div>')).toBeFalsy();
|
|
41
|
-
expect(c(contentModel, '<div><address></address></div>')).toBeFalsy();
|
|
42
|
-
expect(c(contentModel, '<div><header></header></div>')).toBeFalsy();
|
|
43
|
-
});
|
|
44
|
-
});
|