@@ -120,21 +120,27 @@ describe('Highlighter LoadableScript', () => {
120120
121121describe ( 'HighlighterElement' , ( ) => {
122122 let highlighter : HTMLElement
123- const queryFromShadow = < T extends Element > ( selector : string ) : T | null =>
124- ( highlighter . shadowRoot ?. querySelector ( selector ) as T | null ) ?? null
123+ const queryWithin = < T extends Element > ( selector : string ) : T | null =>
124+ ( highlighter . querySelector ( selector ) as T | null ) ?? null
125125
126- const getFromShadow = < T extends Element > ( selector : string ) : T => {
127- const element = queryFromShadow < T > ( selector )
126+ const getWithin = < T extends Element > ( selector : string ) : T => {
127+ const element = queryWithin < T > ( selector )
128128 if ( ! element ) {
129- throw new Error ( `Expected element ${ selector } to exist inside Highlighter shadow DOM ` )
129+ throw new Error ( `Expected element ${ selector } to exist inside Highlighter` )
130130 }
131131 return element
132132 }
133133
134134 const getShareButton = ( platform : string ) : HTMLButtonElement =>
135- getFromShadow < HTMLButtonElement > ( `[data-platform="${ platform } "]` )
135+ getWithin < HTMLButtonElement > ( `.share-button [data-platform="${ platform } "]` )
136136
137- const getShareDialog = ( ) => getFromShadow < HTMLElement > ( '.share-dialog' )
137+ const getShareDialog = ( ) => getWithin < HTMLElement > ( '.share-dialog' )
138+ const getTriggerButton = ( ) => getWithin < HTMLButtonElement > ( '.highlighter__trigger' )
139+ const getWrapper = ( ) => getWithin < HTMLDivElement > ( '.highlighter__wrapper' )
140+ const getHighlightContent = ( element : Element = highlighter ) =>
141+ element . querySelector < HTMLElement > ( '.highlighter__content' )
142+ const getHighlightText = ( element : Element = highlighter ) =>
143+ getHighlightContent ( element ) ?. textContent ?. trim ( ) ?? ''
138144
139145 const getLatestShareEvent = (
140146 listener : ReturnType < typeof vi . fn >
@@ -172,22 +178,22 @@ describe('HighlighterElement', () => {
172178 } )
173179
174180 describe ( 'Rendering' , ( ) => {
175- it ( 'should create shadow DOM' , ( ) => {
176- expect ( highlighter . shadowRoot ) . toBeTruthy ( )
181+ it ( 'should render in the light DOM' , ( ) => {
182+ expect ( highlighter . shadowRoot ) . toBeNull ( )
177183 } )
178184
179185 it ( 'should render share dialog' , ( ) => {
180186 expect ( getShareDialog ( ) ) . toBeTruthy ( )
181187 } )
182188
183189 it ( 'should render all platform buttons' , ( ) => {
184- const buttons = highlighter . shadowRoot ?. querySelectorAll ( '.share-button' )
190+ const buttons = highlighter . querySelectorAll ( '.share-button' )
185191 expect ( buttons ) . toHaveLength ( 6 ) // 5 platforms + copy button
186192 } )
187193
188194 it ( 'should render platform buttons in correct order' , ( ) => {
189- const buttons = highlighter . shadowRoot ?. querySelectorAll ( '.share-button' )
190- const platforms = Array . from ( buttons || [ ] )
195+ const buttons = highlighter . querySelectorAll ( '.share-button' )
196+ const platforms = Array . from ( buttons )
191197 . slice ( 0 , 4 ) // First 4 are social platforms
192198 . map ( btn => btn . getAttribute ( 'data-platform' ) )
193199
@@ -247,17 +253,17 @@ describe('HighlighterElement', () => {
247253
248254 describe ( 'Keyboard Navigation' , ( ) => {
249255 it ( 'should show dialog on Enter key' , async ( ) => {
250- // Trigger Enter key on the host element (using keyup for accessibility )
256+ const trigger = getTriggerButton ( )
251257 const event = new KeyboardEvent ( 'keyup' , { key : 'Enter' , bubbles : true } )
252- highlighter . dispatchEvent ( event )
258+ trigger . dispatchEvent ( event )
253259
254260 expect ( getShareDialog ( ) . getAttribute ( 'aria-hidden' ) ) . toBe ( 'false' )
255261 } )
256262
257263 it ( 'should show dialog on Space key' , async ( ) => {
258- // Trigger Space key on the host element (using keyup for accessibility )
264+ const trigger = getTriggerButton ( )
259265 const event = new KeyboardEvent ( 'keyup' , { key : ' ' , bubbles : true } )
260- highlighter . dispatchEvent ( event )
266+ trigger . dispatchEvent ( event )
261267
262268 expect ( getShareDialog ( ) . getAttribute ( 'aria-hidden' ) ) . toBe ( 'false' )
263269 } )
@@ -267,35 +273,35 @@ describe('HighlighterElement', () => {
267273 highlighter . dispatchEvent ( new MouseEvent ( 'mouseenter' , { bubbles : true } ) )
268274 expect ( getShareDialog ( ) . getAttribute ( 'aria-hidden' ) ) . toBe ( 'false' )
269275
270- // Trigger Escape key (using keyup for accessibility)
276+ // Trigger Escape key on wrapper (using keyup for accessibility)
277+ const wrapper = getWrapper ( )
271278 const event = new KeyboardEvent ( 'keyup' , { key : 'Escape' , bubbles : true } )
272- document . dispatchEvent ( event )
279+ wrapper . dispatchEvent ( event )
273280
274281 expect ( getShareDialog ( ) . getAttribute ( 'aria-hidden' ) ) . toBe ( 'true' )
275282 } )
276283 } )
277284
278285 describe ( 'Text Extraction' , ( ) => {
279286 it ( 'should extract text from slot' , ( ) => {
280- const text = highlighter . textContent ?. trim ( )
281- expect ( text ) . toBe ( 'Test shareable content' )
287+ expect ( getHighlightText ( ) ) . toBe ( 'Test shareable content' )
282288 } )
283289
284- it ( 'should handle empty content' , ( ) => {
290+ it ( 'should handle empty content' , async ( ) => {
285291 const emptyHighlighter = document . createElement ( 'highlighter-element' )
286292 document . body . appendChild ( emptyHighlighter )
287293
288- const text = emptyHighlighter . textContent ?. trim ( )
289- expect ( text ) . toBe ( '' )
294+ await flushMicrotasks ( )
295+ expect ( getHighlightText ( emptyHighlighter ) ) . toBe ( '' )
290296 } )
291297
292- it ( 'should handle nested HTML' , ( ) => {
298+ it ( 'should handle nested HTML' , async ( ) => {
293299 const htmlHighlighter = document . createElement ( 'highlighter-element' )
294300 htmlHighlighter . innerHTML = '<strong>Bold</strong> and <em>italic</em> text'
295301 document . body . appendChild ( htmlHighlighter )
296302
297- const text = htmlHighlighter . textContent ?. trim ( )
298- expect ( text ) . toBe ( 'Bold and italic text' )
303+ await flushMicrotasks ( )
304+ expect ( getHighlightText ( htmlHighlighter ) ) . toBe ( 'Bold and italic text' )
299305 } )
300306 } )
301307
@@ -358,9 +364,7 @@ describe('HighlighterElement', () => {
358364
359365 await flushMicrotasks ( )
360366
361- const twitterButton = specialHighlighter . shadowRoot ?. querySelector ( '[data-platform="twitter"]' ) as
362- | HTMLButtonElement
363- | null
367+ const twitterButton = specialHighlighter . querySelector < HTMLButtonElement > ( '[data-platform="twitter"]' )
364368 if ( ! twitterButton ) {
365369 throw new Error ( 'twitter button missing on special highlighter' )
366370 }
@@ -482,25 +486,26 @@ describe('HighlighterElement', () => {
482486 it ( 'should have proper ARIA attributes on container' , ( ) => {
483487 // The host element is the container
484488 expect ( highlighter . getAttribute ( 'role' ) ) . toBeNull ( ) // Not set by default on custom elements
485- expect ( highlighter . getAttribute ( 'tabindex' ) ) . toBe ( '0' )
489+ expect ( highlighter . getAttribute ( 'tabindex' ) ) . toBeNull ( )
486490 expect ( highlighter . getAttribute ( 'aria-label' ) ) . toBeTruthy ( )
491+
492+ const trigger = getTriggerButton ( )
493+ expect ( trigger . getAttribute ( 'type' ) ) . toBe ( 'button' )
494+ expect ( trigger . getAttribute ( 'aria-label' ) ) . toBe ( 'Share this quote' )
487495 } )
488496
489497 it ( 'should have proper ARIA labels on platform buttons' , ( ) => {
490- const twitterButton = highlighter . shadowRoot ?. querySelector ( '[data-platform="twitter"]' )
491- expect ( twitterButton ?. getAttribute ( 'aria-label' ) ) . toBe ( 'Share on X (Twitter)' )
492-
493- const linkedinButton = highlighter . shadowRoot ?. querySelector ( '[data-platform="linkedin"]' )
494- expect ( linkedinButton ?. getAttribute ( 'aria-label' ) ) . toBe ( 'Share on LinkedIn' )
498+ expect ( getShareButton ( 'twitter' ) . getAttribute ( 'aria-label' ) ) . toBe ( 'Share on X (Twitter)' )
499+ expect ( getShareButton ( 'linkedin' ) . getAttribute ( 'aria-label' ) ) . toBe ( 'Share on LinkedIn' )
495500 } )
496501
497502 it ( 'should toggle aria-hidden on dialog' , ( ) => {
498- const dialog = highlighter . shadowRoot ?. querySelector ( '.share-dialog' )
499- expect ( dialog ? .getAttribute ( 'aria-hidden' ) ) . toBe ( 'true' )
503+ const dialog = getShareDialog ( )
504+ expect ( dialog . getAttribute ( 'aria-hidden' ) ) . toBe ( 'true' )
500505
501506 highlighter . dispatchEvent ( new MouseEvent ( 'mouseenter' , { bubbles : true } ) )
502507
503- expect ( dialog ? .getAttribute ( 'aria-hidden' ) ) . toBe ( 'false' )
508+ expect ( dialog . getAttribute ( 'aria-hidden' ) ) . toBe ( 'false' )
504509 } )
505510 } )
506511
@@ -513,19 +518,13 @@ describe('HighlighterElement', () => {
513518 expect ( ( ) => button . click ( ) ) . not . toThrow ( )
514519 } )
515520
516- it ( 'should handle missing shadowRoot gracefully' , ( ) => {
517- // This is more for coverage - should not happen in practice
518- const element = document . createElement ( 'div' )
519- expect ( element . shadowRoot ) . toBeNull ( )
520- } )
521-
522- it ( 'should handle very long text content' , ( ) => {
521+ it ( 'should handle very long text content' , async ( ) => {
523522 const longHighlighter = document . createElement ( 'highlighter-element' )
524523 longHighlighter . textContent = 'A' . repeat ( 1000 )
525524 document . body . appendChild ( longHighlighter )
526525
527- const text = longHighlighter . textContent ?. trim ( )
528- expect ( text ) . toHaveLength ( 1000 )
526+ await flushMicrotasks ( )
527+ expect ( getHighlightText ( longHighlighter ) ) . toHaveLength ( 1000 )
529528 } )
530529 } )
531530} )
0 commit comments