@@ -115,5 +115,109 @@ describe('lazyDefine', () => {
115115 await animationFrame ( )
116116 expect ( onDefine ) . to . be . callCount ( 1 )
117117 } )
118+
119+ it ( 'waits for element to be added to DOM before observing' , async ( ) => {
120+ const onDefine = spy ( )
121+ lazyDefine ( 'late-visible-element' , onDefine )
122+
123+ await animationFrame ( )
124+ expect ( onDefine ) . to . be . callCount ( 0 )
125+
126+ // Add the element later
127+ await fixture ( html `< late-visible-element data-load-on ="visible "> </ late-visible-element > ` )
128+
129+ await animationFrame ( )
130+ expect ( onDefine ) . to . be . callCount ( 1 )
131+ } )
132+ } )
133+
134+ describe ( 'race condition prevention' , ( ) => {
135+ it ( 'does not fire callbacks multiple times from concurrent scans' , async ( ) => {
136+ const onDefine = spy ( )
137+ lazyDefine ( 'race-test-element' , onDefine )
138+
139+ // Create multiple elements to trigger multiple scans
140+ const el1 = await fixture ( html `< race-test-element > </ race-test-element > ` )
141+ const el2 = await fixture ( html `< race-test-element > </ race-test-element > ` )
142+
143+ await animationFrame ( )
144+ await animationFrame ( )
145+
146+ // Should only be called once despite multiple elements triggering scans
147+ expect ( onDefine ) . to . be . callCount ( 1 )
148+ } )
149+ } )
150+
151+ describe ( 'late registration' , ( ) => {
152+ it ( 'executes callback immediately for already-triggered tags' , async ( ) => {
153+ const onDefine1 = spy ( )
154+ const onDefine2 = spy ( )
155+
156+ // Register and trigger first callback
157+ lazyDefine ( 'late-reg-element' , onDefine1 )
158+ await fixture ( html `< late-reg-element > </ late-reg-element > ` )
159+ await animationFrame ( )
160+ expect ( onDefine1 ) . to . be . callCount ( 1 )
161+
162+ // Register second callback after element is already triggered
163+ lazyDefine ( 'late-reg-element' , onDefine2 )
164+ await animationFrame ( )
165+
166+ // Second callback should be executed immediately
167+ expect ( onDefine2 ) . to . be . callCount ( 1 )
168+ } )
169+ } )
170+
171+ describe ( 'error handling' , ( ) => {
172+ it ( 'handles callback errors without breaking other callbacks' , async ( ) => {
173+ const onDefine1 = spy ( ( ) => {
174+ throw new Error ( 'Test error' )
175+ } )
176+ const onDefine2 = spy ( )
177+
178+ // Suppress global error reporting for this test
179+ const originalReportError = globalThis . reportError
180+ const errors : unknown [ ] = [ ]
181+ globalThis . reportError = ( err : unknown ) => errors . push ( err )
182+
183+ try {
184+ lazyDefine ( 'error-test-element' , onDefine1 )
185+ lazyDefine ( 'error-test-element' , onDefine2 )
186+
187+ await fixture ( html `< error-test-element > </ error-test-element > ` )
188+ await animationFrame ( )
189+
190+ // Both callbacks should be called despite first one throwing
191+ expect ( onDefine1 ) . to . be . callCount ( 1 )
192+ expect ( onDefine2 ) . to . be . callCount ( 1 )
193+
194+ // Error should have been reported
195+ expect ( errors . length ) . to . be . greaterThan ( 0 )
196+ } finally {
197+ globalThis . reportError = originalReportError
198+ }
199+ } )
200+ } )
201+
202+ describe ( 'redundant observe calls' , ( ) => {
203+ it ( 'does not observe the same target multiple times' , async ( ) => {
204+ const onDefine = spy ( )
205+ const el = await fixture ( html `< div > </ div > ` )
206+ const shadowRoot = el . attachShadow ( { mode : 'open' } )
207+
208+ // Observe the same shadow root multiple times
209+ observe ( shadowRoot )
210+ observe ( shadowRoot )
211+ observe ( shadowRoot )
212+
213+ lazyDefine ( 'redundant-test-element' , onDefine )
214+ // eslint-disable-next-line github/unescaped-html-literal
215+ shadowRoot . innerHTML = '<redundant-test-element></redundant-test-element>'
216+
217+ await animationFrame ( )
218+
219+ // Should still only be called once
220+ expect ( onDefine ) . to . be . callCount ( 1 )
221+ } )
118222 } )
119223} )
0 commit comments