@@ -49,6 +49,67 @@ class FakeDocument {
4949 return this . _elementsByTagName . get ( name ) || [ ] ;
5050 }
5151
52+ querySelectorAll ( selector ) {
53+ // Simple selector support for common cases
54+ // Tag selector: "link", "script", etc.
55+ if ( / ^ [ a - z A - Z ] [ a - z A - Z 0 - 9 - ] * $ / . test ( selector ) ) {
56+ return this . getElementsByTagName ( selector ) ;
57+ }
58+ // Class selector: ".class"
59+ if ( selector . startsWith ( "." ) ) {
60+ const className = selector . slice ( 1 ) ;
61+ const allElements = [ ] ;
62+ for ( const elements of this . _elementsByTagName . values ( ) ) {
63+ for ( const element of elements ) {
64+ if ( element . getAttribute ( "class" ) === className ) {
65+ allElements . push ( element ) ;
66+ }
67+ }
68+ }
69+ return allElements ;
70+ }
71+ // ID selector: "#id"
72+ if ( selector . startsWith ( "#" ) ) {
73+ const id = selector . slice ( 1 ) ;
74+ for ( const elements of this . _elementsByTagName . values ( ) ) {
75+ for ( const element of elements ) {
76+ if ( element . getAttribute ( "id" ) === id ) {
77+ return [ element ] ;
78+ }
79+ }
80+ }
81+ return [ ] ;
82+ }
83+ // Attribute selector: "[attr]", "[attr=value]"
84+ if ( selector . startsWith ( "[" ) && selector . endsWith ( "]" ) ) {
85+ const attrSelector = selector . slice ( 1 , - 1 ) ;
86+ const allElements = [ ] ;
87+ if ( attrSelector . includes ( "=" ) ) {
88+ const [ attr , value ] = attrSelector
89+ . split ( "=" )
90+ . map ( ( s ) => s . trim ( ) . replace ( / ^ [ " ' ] | [ " ' ] $ / g, "" ) ) ;
91+ for ( const elements of this . _elementsByTagName . values ( ) ) {
92+ for ( const element of elements ) {
93+ if ( element . getAttribute ( attr ) === value ) {
94+ allElements . push ( element ) ;
95+ }
96+ }
97+ }
98+ } else {
99+ for ( const elements of this . _elementsByTagName . values ( ) ) {
100+ for ( const element of elements ) {
101+ if ( element . getAttribute ( attrSelector ) !== undefined ) {
102+ allElements . push ( element ) ;
103+ }
104+ }
105+ }
106+ }
107+ return allElements ;
108+ }
109+ // Default: return empty array for unsupported selectors
110+ return [ ] ;
111+ }
112+
52113 getComputedStyle ( element ) {
53114 const style = { getPropertyValue } ;
54115 const links = this . getElementsByTagName ( "link" ) ;
@@ -83,8 +144,11 @@ class FakeElement {
83144
84145 _load ( node ) {
85146 if ( node . _type === "link" ) {
86- setTimeout ( ( ) => {
87- if ( node . onload ) node . onload ( { type : "load" , target : node } ) ;
147+ const timer = setTimeout ( ( ) => {
148+ clearTimeout ( timer ) ;
149+ const loadEvent = { type : "load" , target : node } ;
150+ if ( node . onload ) node . onload ( loadEvent ) ;
151+ node . _dispatchEvent ( loadEvent ) ;
88152 } , 100 ) ;
89153 } else if ( node . _type === "script" && this . _document . onScript ) {
90154 Promise . resolve ( ) . then ( ( ) => {
@@ -184,6 +248,81 @@ class FakeElement {
184248 set rel ( value ) {
185249 this . _attributes . rel = value ;
186250 }
251+
252+ addEventListener ( event , handler ) {
253+ if ( ! this . _eventListeners ) {
254+ this . _eventListeners = new Map ( ) ;
255+ }
256+ if ( ! this . _eventListeners . has ( event ) ) {
257+ this . _eventListeners . set ( event , [ ] ) ;
258+ }
259+ this . _eventListeners . get ( event ) . push ( handler ) ;
260+ }
261+
262+ removeEventListener ( event , handler ) {
263+ if ( ! this . _eventListeners ) return ;
264+ const handlers = this . _eventListeners . get ( event ) ;
265+ if ( ! handlers ) return ;
266+ const index = handlers . indexOf ( handler ) ;
267+ if ( index >= 0 ) {
268+ handlers . splice ( index , 1 ) ;
269+ }
270+ }
271+
272+ _dispatchEvent ( event ) {
273+ if ( ! this . _eventListeners ) return ;
274+ const handlers = this . _eventListeners . get ( event . type ) ;
275+ if ( handlers ) {
276+ for ( const handler of handlers ) {
277+ handler ( event ) ;
278+ }
279+ }
280+ }
281+
282+ cloneNode ( deep = false ) {
283+ const cloned = new FakeElement (
284+ this . _document ,
285+ this . _type ,
286+ this . _document . _basePath
287+ ) ;
288+
289+ // Copy attributes
290+ cloned . _attributes = { ...this . _attributes } ;
291+
292+ // Copy src and href
293+ cloned . _src = this . _src ;
294+ cloned . _href = this . _href ;
295+
296+ // For link elements, create a new sheet with the same href
297+ if ( this . _type === "link" && this . sheet ) {
298+ cloned . sheet = new FakeSheet ( cloned , this . _document . _basePath ) ;
299+ if ( this . _href ) {
300+ cloned . href = this . _href ;
301+ }
302+ }
303+
304+ // Copy event handlers if they exist
305+ if ( this . onload ) {
306+ cloned . onload = this . onload ;
307+ }
308+ // Copy event listeners
309+ if ( this . _eventListeners ) {
310+ cloned . _eventListeners = new Map ( ) ;
311+ for ( const [ event , handlers ] of this . _eventListeners . entries ( ) ) {
312+ cloned . _eventListeners . set ( event , [ ...handlers ] ) ;
313+ }
314+ }
315+
316+ // Deep clone children if requested
317+ if ( deep ) {
318+ for ( const child of this . _children ) {
319+ const clonedChild = child . cloneNode ( true ) ;
320+ cloned . appendChild ( clonedChild ) ;
321+ }
322+ }
323+
324+ return cloned ;
325+ }
187326}
188327
189328class FakeSheet {
0 commit comments