From d366c1ab34882c51231465ee2eb9015c2eb541f9 Mon Sep 17 00:00:00 2001 From: btea <2356281422@qq.com> Date: Fri, 10 Feb 2023 15:50:41 +0800 Subject: [PATCH 1/2] #715@patch: Attribute value can contains point. --- .../src/query-selector/SelectorItem.ts | 2 +- .../test/query-selector/QuerySelector.test.ts | 70 +++++++++++++------ .../query-selector/data/QuerySelectorHTML.ts | 1 + 3 files changed, 50 insertions(+), 23 deletions(-) diff --git a/packages/happy-dom/src/query-selector/SelectorItem.ts b/packages/happy-dom/src/query-selector/SelectorItem.ts index bb1a968ad..5a97c415e 100644 --- a/packages/happy-dom/src/query-selector/SelectorItem.ts +++ b/packages/happy-dom/src/query-selector/SelectorItem.ts @@ -41,7 +41,7 @@ export default class SelectorItem { this.isAttribute = !this.isAll && baseSelector.includes('['); // If baseSelector !== selector then some psuedo selector was replaced above this.isPseudo = !this.isAll && baseSelector !== selector; - this.isClass = !this.isAll && new RegExp(CLASS_REGEXP, 'g').test(baseSelector); + this.isClass = !this.isAll && new RegExp(CLASS_REGEXP, 'g').test(baseSelector) && !this.isAttribute; this.tagName = !this.isAll ? baseSelector.match(TAG_NAME_REGEXP) : null; this.tagName = this.tagName ? this.tagName[0].toUpperCase() : null; this.isTagName = this.tagName !== null; diff --git a/packages/happy-dom/test/query-selector/QuerySelector.test.ts b/packages/happy-dom/test/query-selector/QuerySelector.test.ts index b9be1b0d1..0adf0de4e 100644 --- a/packages/happy-dom/test/query-selector/QuerySelector.test.ts +++ b/packages/happy-dom/test/query-selector/QuerySelector.test.ts @@ -19,9 +19,10 @@ describe('QuerySelector', () => { const container = document.createElement('div'); container.innerHTML = QuerySelectorHTML; const elements = container.querySelectorAll('span'); - expect(elements.length).toBe(2); + expect(elements.length).toBe(3); expect(elements[0]).toBe(container.children[0].children[1].children[0]); expect(elements[1]).toBe(container.children[0].children[1].children[1]); + expect(elements[2]).toBe(container.children[0].children[1].children[2]); }); it('Returns a NodeList with the method item().', () => { @@ -44,11 +45,12 @@ describe('QuerySelector', () => { const container = document.createElement('div'); container.innerHTML = QuerySelectorHTML; const elements = container.querySelectorAll('.class1'); - expect(elements.length).toBe(4); + expect(elements.length).toBe(5); expect(elements[0]).toBe(container.children[0]); expect(elements[1]).toBe(container.children[0].children[1]); expect(elements[2]).toBe(container.children[0].children[1].children[0]); expect(elements[3]).toBe(container.children[0].children[1].children[1]); + expect(elements[4]).toBe(container.children[0].children[1].children[2]); }); it('Returns all elements with class name "before:after".', () => { @@ -106,39 +108,43 @@ describe('QuerySelector', () => { const container = document.createElement('div'); container.innerHTML = QuerySelectorHTML; const elements = container.querySelectorAll('.class1.class2'); - expect(elements.length).toBe(4); + expect(elements.length).toBe(5); expect(elements[0]).toBe(container.children[0]); expect(elements[1]).toBe(container.children[0].children[1]); expect(elements[2]).toBe(container.children[0].children[1].children[0]); expect(elements[3]).toBe(container.children[0].children[1].children[1]); + expect(elements[4]).toBe(container.children[0].children[1].children[2]); }); it('Returns all elements matching ".class1 > .class1 > *".', () => { const container = document.createElement('div'); container.innerHTML = QuerySelectorHTML; const elements = container.querySelectorAll('.class1 > .class1 > *'); - expect(elements.length).toBe(2); + expect(elements.length).toBe(3); expect(elements[0]).toBe(container.children[0].children[1].children[0]); expect(elements[1]).toBe(container.children[0].children[1].children[1]); + expect(elements[2]).toBe(container.children[0].children[1].children[2]); }); it('Returns all elements matching "div > div > span".', () => { const container = document.createElement('div'); container.innerHTML = QuerySelectorHTML; const elements = container.querySelectorAll('div > div > span'); - expect(elements.length).toBe(2); + expect(elements.length).toBe(3); expect(elements[0]).toBe(container.children[0].children[1].children[0]); expect(elements[1]).toBe(container.children[0].children[1].children[1]); + expect(elements[2]).toBe(container.children[0].children[1].children[2]); }); it('Returns all elements matching "div > div > .class1.class2".', () => { const container = document.createElement('div'); container.innerHTML = QuerySelectorHTML; const elements = container.querySelectorAll('div > div > .class1.class2'); - expect(elements.length).toBe(3); + expect(elements.length).toBe(4); expect(elements[0]).toBe(container.children[0].children[1]); expect(elements[1]).toBe(container.children[0].children[1].children[0]); expect(elements[2]).toBe(container.children[0].children[1].children[1]); + expect(elements[3]).toBe(container.children[0].children[1].children[2]); }); it('Returns all elements with tag name and class "span.class1".', () => { @@ -146,9 +152,10 @@ describe('QuerySelector', () => { container.innerHTML = QuerySelectorHTML; const elements = container.querySelectorAll('span.class1'); - expect(elements.length).toBe(2); + expect(elements.length).toBe(3); expect(elements[0]).toBe(container.children[0].children[1].children[0]); expect(elements[1]).toBe(container.children[0].children[1].children[1]); + expect(elements[2]).toBe(container.children[0].children[1].children[2]); }); it('Returns all elements with matching attributes using "[attr1="value1"]".', () => { @@ -161,6 +168,15 @@ describe('QuerySelector', () => { expect(elements[1]).toBe(container.children[0].children[1].children[1]); }); + it('Returns all elements with matching attributes using "[attr1="word1.word2"]".', () => { + const container = document.createElement('div'); + container.innerHTML = QuerySelectorHTML; + const elements = container.querySelectorAll('[attr1="word1.word2"]'); + + expect(elements.length).toBe(1); + expect(elements[0]).toBe(container.children[0].children[1].children[2]); + }); + it('Returns all elements with multiple matching attributes using "[attr1="value1"][attr2="word1 word2"]".', () => { const container = document.createElement('div'); container.innerHTML = QuerySelectorHTML; @@ -205,9 +221,10 @@ describe('QuerySelector', () => { container.innerHTML = QuerySelectorHTML.replace(/ attr1/gm, '_attr1'); const elements = container.querySelectorAll('span[_attr1]'); - expect(elements.length).toBe(2); + expect(elements.length).toBe(3); expect(elements[0]).toBe(container.children[0].children[1].children[0]); expect(elements[1]).toBe(container.children[0].children[1].children[1]); + expect(elements[2]).toBe(container.children[0].children[1].children[2]); }); it('Returns all elements with tag name and multiple matching attributes using "span[attr1="value1"][attr2="word1 word2"]".', () => { @@ -246,11 +263,12 @@ describe('QuerySelector', () => { container.innerHTML = QuerySelectorHTML; const elements = container.querySelectorAll('[class~="class2"]'); - expect(elements.length).toBe(4); + expect(elements.length).toBe(5); expect(elements[0]).toBe(container.children[0]); expect(elements[1]).toBe(container.children[0].children[1]); expect(elements[2]).toBe(container.children[0].children[1].children[0]); expect(elements[3]).toBe(container.children[0].children[1].children[1]); + expect(elements[4]).toBe(container.children[0].children[1].children[2]); }); it('Returns all elements with an attribute value starting with the specified word using "[class|="class1"]".', () => { @@ -258,11 +276,12 @@ describe('QuerySelector', () => { container.innerHTML = QuerySelectorHTML; const elements = container.querySelectorAll('[class|="class1"]'); - expect(elements.length).toBe(4); + expect(elements.length).toBe(5); expect(elements[0]).toBe(container.children[0]); expect(elements[1]).toBe(container.children[0].children[1]); expect(elements[2]).toBe(container.children[0].children[1].children[0]); expect(elements[3]).toBe(container.children[0].children[1].children[1]); + expect(elements[4]).toBe(container.children[0].children[1].children[2]); }); it('Returns all elements with an attribute value that begins with a specified value using "[class^="cl"]".', () => { @@ -270,11 +289,12 @@ describe('QuerySelector', () => { container.innerHTML = QuerySelectorHTML; const elements = container.querySelectorAll('[class^="cl"]'); - expect(elements.length).toBe(4); + expect(elements.length).toBe(5); expect(elements[0]).toBe(container.children[0]); expect(elements[1]).toBe(container.children[0].children[1]); expect(elements[2]).toBe(container.children[0].children[1].children[0]); expect(elements[3]).toBe(container.children[0].children[1].children[1]); + expect(elements[4]).toBe(container.children[0].children[1].children[2]); }); it('Returns all elements with an attribute value that ends with a specified value using "[class$="ss2"]".', () => { @@ -282,11 +302,12 @@ describe('QuerySelector', () => { container.innerHTML = QuerySelectorHTML; const elements = container.querySelectorAll('[class$="ss2"]'); - expect(elements.length).toBe(4); + expect(elements.length).toBe(5); expect(elements[0]).toBe(container.children[0]); expect(elements[1]).toBe(container.children[0].children[1]); expect(elements[2]).toBe(container.children[0].children[1].children[0]); expect(elements[3]).toBe(container.children[0].children[1].children[1]); + expect(elements[4]).toBe(container.children[0].children[1].children[2]); }); it('Returns all elements with an attribute value that contains a specified value using "[class*="s1 cl"]".', () => { @@ -294,11 +315,12 @@ describe('QuerySelector', () => { container.innerHTML = QuerySelectorHTML; const elements = container.querySelectorAll('[class*="s1 cl"]'); - expect(elements.length).toBe(4); + expect(elements.length).toBe(5); expect(elements[0]).toBe(container.children[0]); expect(elements[1]).toBe(container.children[0].children[1]); expect(elements[2]).toBe(container.children[0].children[1].children[0]); expect(elements[3]).toBe(container.children[0].children[1].children[1]); + expect(elements[4]).toBe(container.children[0].children[1].children[2]); }); it('Returns all elements with an attribute value that contains a specified value using "[class*="s1 cl"]" or matches exactly a value using "[attr1="value1"]".', () => { @@ -306,25 +328,28 @@ describe('QuerySelector', () => { container.innerHTML = QuerySelectorHTML; const elements = container.querySelectorAll('[class*="s1 cl"], [attr1="value1"]'); - expect(elements.length).toBe(4); + expect(elements.length).toBe(5); expect(elements[0]).toBe(container.children[0]); expect(elements[1]).toBe(container.children[0].children[1]); expect(elements[2]).toBe(container.children[0].children[1].children[0]); expect(elements[3]).toBe(container.children[0].children[1].children[1]); + expect(elements[4]).toBe(container.children[0].children[1].children[2]); }); it('Returns all elements with an attribute value that contains a specified value using "[class*="s1 cl"]" or has the tag "b".', () => { const container = document.createElement('div'); container.innerHTML = QuerySelectorHTML; const elements = container.querySelectorAll('[class*="s1 cl"], h1'); - - expect(elements.length).toBe(6); + const children = container.children; + expect(children.length).toBe(2); + expect(elements.length).toBe(7); expect(elements[0]).toBe(container.children[0]); expect(elements[1]).toBe(container.children[0].children[1]); expect(elements[2]).toBe(container.children[0].children[1].children[0]); expect(elements[3]).toBe(container.children[0].children[1].children[1]); - expect(elements[4]).toBe(container.children[0].children[0]); - expect(elements[5]).toBe(container.children[1].children[0]); + expect(elements[4]).toBe(container.children[0].children[1].children[2]); + expect(elements[5]).toBe(container.children[0].children[0]); + expect(elements[6]).toBe(container.children[1].children[0]); }); it('Returns all span elements matching ":first-child".', () => { @@ -355,7 +380,7 @@ describe('QuerySelector', () => { expect(elements.length).toBe(4); expect(elements[0]).toBe(container.children[0].children[1]); - expect(elements[1]).toBe(container.children[0].children[1].children[1]); + expect(elements[1]).toBe(container.children[0].children[1].children[2]); expect(elements[2]).toBe(container.children[1]); expect(elements[3]).toBe(container.children[1].children[0]); }); @@ -366,7 +391,7 @@ describe('QuerySelector', () => { const elements = container.querySelectorAll('span:last-child'); expect(elements.length).toBe(1); - expect(elements[0]).toBe(container.children[0].children[1].children[1]); + expect(elements[0]).toBe(container.children[0].children[1].children[2]); }); it('Returns all span elements matching ":only-child".', () => { @@ -408,7 +433,7 @@ describe('QuerySelector', () => { expect(elements.length).toBe(5); expect(elements[0]).toBe(container.children[0].children[0]); expect(elements[1]).toBe(container.children[0].children[1]); - expect(elements[2]).toBe(container.children[0].children[1].children[1]); + expect(elements[2]).toBe(container.children[0].children[1].children[2]); expect(elements[3]).toBe(container.children[1]); expect(elements[4]).toBe(container.children[1].children[0]); }); @@ -418,8 +443,9 @@ describe('QuerySelector', () => { container.innerHTML = QuerySelectorHTML; const elements = container.querySelectorAll('span:not([type=hidden])'); - expect(elements.length).toBe(1); + expect(elements.length).toBe(2); expect(elements[0]).toBe(container.children[0].children[1].children[1]); + expect(elements[1]).toBe(container.children[0].children[1].children[2]); }); it('Returns all elements matching ".foo:not(.bar)".', () => { diff --git a/packages/happy-dom/test/query-selector/data/QuerySelectorHTML.ts b/packages/happy-dom/test/query-selector/data/QuerySelectorHTML.ts index 8baa25dee..dfb5663ee 100644 --- a/packages/happy-dom/test/query-selector/data/QuerySelectorHTML.ts +++ b/packages/happy-dom/test/query-selector/data/QuerySelectorHTML.ts @@ -6,6 +6,7 @@ export default `
Span1 Span2 + Span3
From f8a2bcb6d6b6634397a7624bdfadc1556a1e85e5 Mon Sep 17 00:00:00 2001 From: btea <2356281422@qq.com> Date: Fri, 10 Feb 2023 16:03:51 +0800 Subject: [PATCH 2/2] #751@patch: Format. --- packages/happy-dom/src/query-selector/SelectorItem.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/happy-dom/src/query-selector/SelectorItem.ts b/packages/happy-dom/src/query-selector/SelectorItem.ts index 5a97c415e..cca4cb5c0 100644 --- a/packages/happy-dom/src/query-selector/SelectorItem.ts +++ b/packages/happy-dom/src/query-selector/SelectorItem.ts @@ -41,7 +41,8 @@ export default class SelectorItem { this.isAttribute = !this.isAll && baseSelector.includes('['); // If baseSelector !== selector then some psuedo selector was replaced above this.isPseudo = !this.isAll && baseSelector !== selector; - this.isClass = !this.isAll && new RegExp(CLASS_REGEXP, 'g').test(baseSelector) && !this.isAttribute; + this.isClass = + !this.isAll && new RegExp(CLASS_REGEXP, 'g').test(baseSelector) && !this.isAttribute; this.tagName = !this.isAll ? baseSelector.match(TAG_NAME_REGEXP) : null; this.tagName = this.tagName ? this.tagName[0].toUpperCase() : null; this.isTagName = this.tagName !== null;