From e72d150c74301763e634ec18b16a03bf6a1d7d7d Mon Sep 17 00:00:00 2001 From: "asamuzaK (Kazz)" Date: Sat, 16 Mar 2024 19:46:12 +0900 Subject: [PATCH 1/3] Add support for `:defined` pseudo class --- src/js/finder.js | 17 ++++- test/finder.test.js | 154 ++++++++++++++++++++++++++++++++++++-------- test/wpt.test.js | 116 ++++++++++++++++++++++++++++++++- 3 files changed, 258 insertions(+), 29 deletions(-) diff --git a/src/js/finder.js b/src/js/finder.js index 1509720f..7273a585 100644 --- a/src/js/finder.js +++ b/src/js/finder.js @@ -1497,6 +1497,22 @@ export class Finder { } break; } + case 'defined': { + const attr = node.getAttribute('is'); + if (attr) { + if (isCustomElementName(attr) && + this.#window.customElements.get(attr)) { + matched.add(node); + } + } else if (isCustomElementName(localName)) { + if (this.#window.customElements.get(localName)) { + matched.add(node); + } + } else if (node instanceof this.#window.HTMLElement) { + matched.add(node); + } + break; + } case 'host': case 'host-context': { // ignore @@ -1518,7 +1534,6 @@ export class Finder { case 'blank': case 'buffering': case 'current': - case 'defined': case 'focus-visible': case 'fullscreen': case 'future': diff --git a/test/finder.test.js b/test/finder.test.js index 5e4152f5..a0eb704f 100644 --- a/test/finder.test.js +++ b/test/finder.test.js @@ -6554,16 +6554,85 @@ describe('Finder', () => { ], 'result'); }); + it('should get matched node', () => { + const leaf = { + children: null, + name: 'defined', + type: SELECTOR_PSEUDO_CLASS + }; + const node = document.createElement('p'); + document.getElementById('div0').appendChild(node); + const finder = new Finder(window); + finder._setup(':defined', node); + const res = finder._matchPseudoClassSelector(leaf, node); + assert.deepEqual([...res], [ + node + ], 'result'); + }); + + it('should get matched node', () => { + const leaf = { + children: null, + name: 'defined', + type: SELECTOR_PSEUDO_CLASS + }; + const node = document.createElement('asdf'); + document.getElementById('div0').appendChild(node); + const finder = new Finder(window); + finder._setup(':defined', node); + const res = finder._matchPseudoClassSelector(leaf, node); + assert.deepEqual([...res], [ + node + ], 'result'); + }); + + it('should get matched node', () => { + const leaf = { + children: null, + name: 'defined', + type: SELECTOR_PSEUDO_CLASS + }; + window.customElements.define('sw-rey', + class extends window.HTMLElement {}); + const node = document.createElement('sw-rey'); + document.getElementById('div0').appendChild(node); + const finder = new Finder(window); + finder._setup(':defined', node); + const res = finder._matchPseudoClassSelector(leaf, node); + assert.deepEqual([...res], [ + node + ], 'result'); + }); + + it('should get matched node', () => { + const leaf = { + children: null, + name: 'defined', + type: SELECTOR_PSEUDO_CLASS + }; + window.customElements.define('sw-finn', + class extends window.HTMLElement {}, { extends: 'p' }); + const node = document.createElement('p'); + node.setAttribute('is', 'sw-finn'); + document.getElementById('div0').appendChild(node); + const finder = new Finder(window); + finder._setup(':defined', node); + const res = finder._matchPseudoClassSelector(leaf, node); + assert.deepEqual([...res], [ + node + ], 'result'); + }); + it('should not match', () => { const leaf = { children: null, - name: 'host', + name: 'defined', type: SELECTOR_PSEUDO_CLASS }; - const node = document.createElement('div'); + const node = document.createElement('sw-han'); document.getElementById('div0').appendChild(node); const finder = new Finder(window); - finder._setup(':host', node); + finder._setup(':defined', node); const res = finder._matchPseudoClassSelector(leaf, node); assert.deepEqual([...res], [], 'result'); }); @@ -6571,59 +6640,89 @@ describe('Finder', () => { it('should not match', () => { const leaf = { children: null, - name: 'host-context', + name: 'defined', type: SELECTOR_PSEUDO_CLASS }; - const node = document.createElement('div'); + const node = document.createElement('p'); + node.setAttribute('is', 'sw-luke'); document.getElementById('div0').appendChild(node); const finder = new Finder(window); - finder._setup(':host-context', node); + finder._setup(':defined', node); const res = finder._matchPseudoClassSelector(leaf, node); assert.deepEqual([...res], [], 'result'); }); - // legacy pseudo-element it('should not match', () => { const leaf = { children: null, - name: 'after', + name: 'defined', + type: SELECTOR_PSEUDO_CLASS + }; + const node = document.createElement('p'); + node.setAttribute('is', 'asdf'); + document.getElementById('div0').appendChild(node); + const finder = new Finder(window); + finder._setup(':defined', node); + const res = finder._matchPseudoClassSelector(leaf, node); + assert.deepEqual([...res], [], 'result'); + }); + + it('should get matched node', () => { + const leaf = { + children: null, + name: 'defined', + type: SELECTOR_PSEUDO_CLASS + }; + window.customElements.define('foo-', class extends window.HTMLElement {}); + const node = document.createElement('foo-'); + document.getElementById('div0').appendChild(node); + const finder = new Finder(window); + finder._setup(':defined', node); + const res = finder._matchPseudoClassSelector(leaf, node); + assert.deepEqual([...res], [ + node + ], 'result'); + }); + + it('should not match', () => { + const leaf = { + children: null, + name: 'host', type: SELECTOR_PSEUDO_CLASS }; const node = document.createElement('div'); document.getElementById('div0').appendChild(node); const finder = new Finder(window); - finder._setup(':after', node); + finder._setup(':host', node); const res = finder._matchPseudoClassSelector(leaf, node); assert.deepEqual([...res], [], 'result'); }); - it('should throw', () => { + it('should not match', () => { const leaf = { children: null, - name: 'after', + name: 'host-context', type: SELECTOR_PSEUDO_CLASS }; const node = document.createElement('div'); document.getElementById('div0').appendChild(node); const finder = new Finder(window); - finder._setup(':after', node, { - warn: true - }); - assert.throws(() => finder._matchPseudoClassSelector(leaf, node), - DOMException, 'Unsupported pseudo-element ::after'); + finder._setup(':host-context', node); + const res = finder._matchPseudoClassSelector(leaf, node); + assert.deepEqual([...res], [], 'result'); }); - // not supported + // legacy pseudo-element it('should not match', () => { const leaf = { children: null, - name: 'active', + name: 'after', type: SELECTOR_PSEUDO_CLASS }; const node = document.createElement('div'); document.getElementById('div0').appendChild(node); const finder = new Finder(window); - finder._setup(':active', node); + finder._setup(':after', node); const res = finder._matchPseudoClassSelector(leaf, node); assert.deepEqual([...res], [], 'result'); }); @@ -6631,29 +6730,30 @@ describe('Finder', () => { it('should throw', () => { const leaf = { children: null, - name: 'active', + name: 'after', type: SELECTOR_PSEUDO_CLASS }; const node = document.createElement('div'); document.getElementById('div0').appendChild(node); const finder = new Finder(window); - finder._setup(':active', node, { + finder._setup(':after', node, { warn: true }); assert.throws(() => finder._matchPseudoClassSelector(leaf, node), - DOMException, 'Unsupported pseudo-class :active'); + DOMException, 'Unsupported pseudo-element ::after'); }); + // not supported it('should not match', () => { const leaf = { children: null, - name: 'defined', + name: 'active', type: SELECTOR_PSEUDO_CLASS }; const node = document.createElement('div'); document.getElementById('div0').appendChild(node); const finder = new Finder(window); - finder._setup(':defined', node); + finder._setup(':active', node); const res = finder._matchPseudoClassSelector(leaf, node); assert.deepEqual([...res], [], 'result'); }); @@ -6661,17 +6761,17 @@ describe('Finder', () => { it('should throw', () => { const leaf = { children: null, - name: 'defined', + name: 'active', type: SELECTOR_PSEUDO_CLASS }; const node = document.createElement('div'); document.getElementById('div0').appendChild(node); const finder = new Finder(window); - finder._setup(':defined', node, { + finder._setup(':active', node, { warn: true }); assert.throws(() => finder._matchPseudoClassSelector(leaf, node), - DOMException, 'Unsupported pseudo-class :defined'); + DOMException, 'Unsupported pseudo-class :active'); }); // unknown diff --git a/test/wpt.test.js b/test/wpt.test.js index 9a4e1e09..58bbcda6 100644 --- a/test/wpt.test.js +++ b/test/wpt.test.js @@ -95,15 +95,17 @@ describe('local wpt test cases', () => { }; } }; - let document; + let window, document; beforeEach(() => { const dom = new JSDOM(domStr, domOpt); + window = dom.window; document = dom.window.document; for (const key of globalKeys) { global[key] = dom.window[key]; } }); afterEach(() => { + window = null; document = null; for (const key of globalKeys) { delete global[key]; @@ -2306,6 +2308,118 @@ describe('local wpt test cases', () => { }); }); + describe('css/selectors/invalidation/defined.html', () => { + it('should not match', () => { + const html = `
+ +
+ +
+
+
+
+
+
`; + document.body.innerHTML = html; + const node = document.getElementById('a1'); + const res = node.matches(':defined'); + assert.isFalse(res, 'result'); + }); + + it('should get matched node', () => { + const html = `
+ +
+ +
+
+
+
+
+
`; + document.body.innerHTML = html; + window.customElements.define('elucidate-late', + class ElucidateLate extends window.HTMLElement { + constructor() { + super(); + } + }); + const node = document.getElementById('a1'); + const res = node.matches(':defined'); + assert.isTrue(res, 'result'); + }); + + it('should get matched node', () => { + const html = `
+ +
+ +
+
+
+
+
+
`; + document.body.innerHTML = html; + window.customElements.define('elucidate-late', + class ElucidateLate extends window.HTMLElement { + constructor() { + super(); + } + }); + const node = document.getElementById('b1'); + const res = node.matches(':defined + #b1'); + assert.isTrue(res, 'result'); + }); + + it('should get matched node', () => { + const html = `
+ +
+ +
+
+
+
+
+
`; + document.body.innerHTML = html; + window.customElements.define('elucidate-late', + class ElucidateLate extends window.HTMLElement { + constructor() { + super(); + } + }); + const node = document.getElementById('c1'); + const res = node.matches(':defined > #c1'); + assert.isTrue(res, 'result'); + }); + + + it('should get matched node', () => { + const html = `
+ +
+ +
+
+
+
+
+
`; + document.body.innerHTML = html; + window.customElements.define('elucidate-late', + class ElucidateLate extends window.HTMLElement { + constructor() { + super(); + } + }); + const node = document.getElementById('d1'); + const res = node.matches('div + :defined + * #d1'); + assert.isTrue(res, 'result'); + }); + }); + describe('jsdom: to-port-to-wpts/query-selector-all.js', () => { it('should get matched node(s)', () => { const html = ` From a4dfe167ba0c0d921629b6a062f6ae95d976d430 Mon Sep 17 00:00:00 2001 From: "asamuzaK (Kazz)" Date: Sat, 16 Mar 2024 19:48:00 +0900 Subject: [PATCH 2/3] Update wpt.test.js --- test/wpt.test.js | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/test/wpt.test.js b/test/wpt.test.js index 58bbcda6..cdea2aff 100644 --- a/test/wpt.test.js +++ b/test/wpt.test.js @@ -2339,11 +2339,7 @@ describe('local wpt test cases', () => { `; document.body.innerHTML = html; window.customElements.define('elucidate-late', - class ElucidateLate extends window.HTMLElement { - constructor() { - super(); - } - }); + class ElucidateLate extends window.HTMLElement {}); const node = document.getElementById('a1'); const res = node.matches(':defined'); assert.isTrue(res, 'result'); @@ -2362,11 +2358,7 @@ describe('local wpt test cases', () => { `; document.body.innerHTML = html; window.customElements.define('elucidate-late', - class ElucidateLate extends window.HTMLElement { - constructor() { - super(); - } - }); + class ElucidateLate extends window.HTMLElement {}); const node = document.getElementById('b1'); const res = node.matches(':defined + #b1'); assert.isTrue(res, 'result'); @@ -2385,17 +2377,12 @@ describe('local wpt test cases', () => { `; document.body.innerHTML = html; window.customElements.define('elucidate-late', - class ElucidateLate extends window.HTMLElement { - constructor() { - super(); - } - }); + class ElucidateLate extends window.HTMLElement {}); const node = document.getElementById('c1'); const res = node.matches(':defined > #c1'); assert.isTrue(res, 'result'); }); - it('should get matched node', () => { const html = `
@@ -2409,11 +2396,7 @@ describe('local wpt test cases', () => {
`; document.body.innerHTML = html; window.customElements.define('elucidate-late', - class ElucidateLate extends window.HTMLElement { - constructor() { - super(); - } - }); + class ElucidateLate extends window.HTMLElement {}); const node = document.getElementById('d1'); const res = node.matches('div + :defined + * #d1'); assert.isTrue(res, 'result'); From c253a5097256e76c3ac120d968ce6fe9ef0b2dda Mon Sep 17 00:00:00 2001 From: "asamuzaK (Kazz)" Date: Sat, 16 Mar 2024 19:50:24 +0900 Subject: [PATCH 3/3] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9b1f4893..1216072c 100644 --- a/README.md +++ b/README.md @@ -111,7 +111,7 @@ Returns **[Array][62]<([object][60] \| [undefined][63])>** array of matched n |E\[foo$="bar"\]|✓| | |E\[foo*="bar"\]|✓| | |E\[foo\|="en"\]|✓| | -|E:defined|Unsupported| | +|E:defined|✓| | |E:dir(ltr)|✓| | |E:lang(en)|Partially supported|Comma-separated list of language codes, e.g. `:lang(en, fr)`, is not yet supported.| |E:any‑link|✓| |