[READ-ONLY] Mirror of https://github.com/vitest-dev/vitest. Next generation testing framework powered by Vite. vitest.dev
test testing-tools vite
12

Configure Feed

Select the types of activity you want to include in your feed.

fix(browser): Improve unique CSS selector generation (#6243)

Co-authored-by: Zack Voase <zvoase@netflix.com>

authored by

Zack Voase
Zack Voase
and committed by
GitHub
(Jul 30, 2024, 9:08 AM +0200) e7acd0cf 073a50c9

+56 -5
+21
test/browser/test/userEvent.test.ts
··· 70 70 71 71 expect(onClick).toHaveBeenCalled() 72 72 }) 73 + 74 + test('clicks a button with complex HTML ID', async () => { 75 + const container = document.createElement('div') 76 + // This is similar to unique IDs generated by React's useId() 77 + container.id = ':r3:' 78 + const button = document.createElement('button') 79 + // Use uppercase and special characters 80 + button.id = 'A:Button' 81 + button.textContent = 'Click me' 82 + container.appendChild(button) 83 + document.body.appendChild(container) 84 + 85 + const onClick = vi.fn() 86 + const dblClick = vi.fn() 87 + button.addEventListener('click', onClick) 88 + 89 + await userEvent.click(button) 90 + 91 + expect(onClick).toHaveBeenCalled() 92 + expect(dblClick).not.toHaveBeenCalled() 93 + }) 73 94 }) 74 95 75 96 describe('userEvent.dblClick', () => {
+35 -5
packages/browser/src/client/tester/context.ts
··· 32 32 return getUniqueCssSelector(element) 33 33 } 34 34 35 + function escapeIdForCSSSelector(id: string) { 36 + return id 37 + .split('') 38 + .map((char) => { 39 + const code = char.charCodeAt(0) 40 + 41 + if (char === ' ' || char === '#' || char === '.' || char === ':' || char === '[' || char === ']' || char === '>' || char === '+' || char === '~' || char === '\\') { 42 + // Escape common special characters with backslashes 43 + return `\\${char}` 44 + } 45 + else if (code >= 0x10000) { 46 + // Unicode escape for characters outside the BMP 47 + return `\\${code.toString(16).toUpperCase().padStart(6, '0')} ` 48 + } 49 + else if (code < 0x20 || code === 0x7F) { 50 + // Non-printable ASCII characters (0x00-0x1F and 0x7F) are escaped 51 + return `\\${code.toString(16).toUpperCase().padStart(2, '0')} ` 52 + } 53 + else if (code >= 0x80) { 54 + // Non-ASCII characters (0x80 and above) are escaped 55 + return `\\${code.toString(16).toUpperCase().padStart(2, '0')} ` 56 + } 57 + else { 58 + // Allowable characters are used directly 59 + return char 60 + } 61 + }) 62 + .join('') 63 + } 64 + 35 65 function getUniqueCssSelector(el: Element) { 36 66 const path = [] 37 67 let parent: null | ParentNode ··· 44 74 45 75 const tag = el.tagName 46 76 if (el.id) { 47 - path.push(`#${el.id}`) 77 + path.push(`#${escapeIdForCSSSelector(el.id)}`) 48 78 } 49 79 else if (!el.nextElementSibling && !el.previousElementSibling) { 50 - path.push(tag) 80 + path.push(tag.toLowerCase()) 51 81 } 52 82 else { 53 83 let index = 0 ··· 65 95 } 66 96 67 97 if (sameTagSiblings > 1) { 68 - path.push(`${tag}:nth-child(${elementIndex})`) 98 + path.push(`${tag.toLowerCase()}:nth-child(${elementIndex})`) 69 99 } 70 100 else { 71 - path.push(tag) 101 + path.push(tag.toLowerCase()) 72 102 } 73 103 } 74 104 el = parent as Element 75 105 }; 76 - return `${provider === 'webdriverio' && hasShadowRoot ? '>>>' : ''}${path.reverse().join(' > ')}`.toLowerCase() 106 + return `${provider === 'webdriverio' && hasShadowRoot ? '>>>' : ''}${path.reverse().join(' > ')}` 77 107 } 78 108 79 109 function getParent(el: Element) {