Skip to content

Commit

Permalink
Merge pull request #727 from btea/task/715-attribute-contain-point
Browse files Browse the repository at this point in the history
#715@patch: Attribute value can contains point.
  • Loading branch information
capricorn86 committed Feb 11, 2023
2 parents 3967133 + 705b83f commit 47dada8
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 23 deletions.
3 changes: 2 additions & 1 deletion packages/happy-dom/src/query-selector/SelectorItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.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;
Expand Down
70 changes: 48 additions & 22 deletions packages/happy-dom/test/query-selector/QuerySelector.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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().', () => {
Expand All @@ -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".', () => {
Expand Down Expand Up @@ -106,49 +108,54 @@ 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".', () => {
const container = document.createElement('div');
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"]".', () => {
Expand All @@ -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;
Expand Down Expand Up @@ -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"]".', () => {
Expand Down Expand Up @@ -246,85 +263,93 @@ 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"]".', () => {
const container = document.createElement('div');
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"]".', () => {
const container = document.createElement('div');
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"]".', () => {
const container = document.createElement('div');
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"]".', () => {
const container = document.createElement('div');
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"]".', () => {
const container = document.createElement('div');
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".', () => {
Expand Down Expand Up @@ -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]);
});
Expand All @@ -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".', () => {
Expand Down Expand Up @@ -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]);
});
Expand All @@ -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)".', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export default `
<div class="class1 class2">
<span class="class1 class2" attr1="value1" attr2="word1 word2" attr3="bracket[]bracket" type="hidden">Span1</span>
<span class="class1 class2" attr1="value1">Span2</span>
<span class="class1 class2" attr1="word1.word2">Span3</span>
</div>
</div>
<div>
Expand Down

0 comments on commit 47dada8

Please sign in to comment.