@@ -10,6 +10,7 @@ const path = require('node:path');
1010const puppeteer = require ( 'puppeteer' ) ;
1111
1212const TARGET_UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36' ;
13+ const JQUERY_SOURCE = fs . readFileSync ( require . resolve ( 'jquery' ) , 'utf8' ) ;
1314
1415function run ( cmd , args , options = { } ) {
1516 const result = childProcess . spawnSync ( cmd , args , {
@@ -163,6 +164,8 @@ function createTargetServer(requests) {
163164 window.__templateLinkFixture = { error: err && err.message || String(err) };
164165 }
165166 </script>
167+ <script src="/jquery.js"></script>
168+ <script src="/jquery-fixture.js"></script>
166169 <script src="/rewrite-fixture.js"></script>
167170 <script type="module" src="/module-worker.js"></script>
168171 </body></html>` ) ;
@@ -187,6 +190,62 @@ function createTargetServer(requests) {
187190 window.postMessage({ type: 'gtm-loaded', href: location.href }, location.origin);` ) ;
188191 return ;
189192 }
193+ if ( url . pathname === '/jquery.js' ) {
194+ res . writeHead ( 200 , { 'Content-Type' : 'text/javascript; charset=utf-8' , 'Cache-Control' : 'no-store' } ) ;
195+ res . end ( JQUERY_SOURCE ) ;
196+ return ;
197+ }
198+ if ( url . pathname === '/jquery-fixture.js' ) {
199+ res . writeHead ( 200 , { 'Content-Type' : 'text/javascript; charset=utf-8' , 'Cache-Control' : 'no-store' } ) ;
200+ res . end ( `(($) => {
201+ const root = $('<section id="jquery-fixture-root"><button class="trigger">Go</button><ul><li class="item">one</li><li class="item">two</li></ul></section>').appendTo(document.body);
202+ const found = root.find('.item');
203+ const ended = found.end();
204+ let delegated = 0;
205+ root.on('click', '.trigger', function() {
206+ delegated++;
207+ $(this).data('clicked', true).attr('data-clicked', 'yes');
208+ });
209+ root.find('.trigger').trigger('click');
210+ root.append($.parseHTML('<div class="parsed"><span>parsed</span></div>'));
211+ const deferred = $.Deferred();
212+ const ajax = $.ajax({ url: '/jquery-ajax.json', dataType: 'json' });
213+ const script = $.getScript('/jquery-plugin.js');
214+ $.globalEval('window.__jqueryGlobalEvalHref = location.href;');
215+ deferred.resolve('resolved');
216+ $.when(deferred, ajax, script).done(function(deferredValue, ajaxValue) {
217+ const ajaxData = Array.isArray(ajaxValue) ? ajaxValue[0] : ajaxValue;
218+ window.__jqueryFixture = {
219+ ready: true,
220+ version: $.fn.jquery,
221+ selectorText: found.map(function(_, el) { return $(el).text(); }).get().join(','),
222+ endMatchesRoot: ended[0] === root[0],
223+ delegated,
224+ dataClicked: root.find('.trigger').data('clicked') === true,
225+ attrClicked: root.find('.trigger').attr('data-clicked'),
226+ parsedText: root.find('.parsed span').text(),
227+ param: $.param({ a: 1, b: ['x', 'y'] }),
228+ ajaxData,
229+ plugin: window.__jqueryPlugin || null,
230+ globalEvalHref: window.__jqueryGlobalEvalHref || null,
231+ locationHref: window.location.href
232+ };
233+ }).fail(function(xhr, status, err) {
234+ window.__jqueryFixture = { ready: false, error: String(err || status || 'jquery-failed') };
235+ });
236+ })(jQuery);` ) ;
237+ return ;
238+ }
239+ if ( url . pathname === '/jquery-ajax.json' ) {
240+ res . writeHead ( 200 , { 'Content-Type' : 'application/json; charset=utf-8' , 'Cache-Control' : 'no-store' } ) ;
241+ res . end ( JSON . stringify ( { ok : true , path : '/jquery-ajax.json' } ) ) ;
242+ return ;
243+ }
244+ if ( url . pathname === '/jquery-plugin.js' ) {
245+ res . writeHead ( 200 , { 'Content-Type' : 'text/javascript; charset=utf-8' , 'Cache-Control' : 'no-store' } ) ;
246+ res . end ( `window.__jqueryPlugin = { loaded: true, href: location.href, jquery: !!window.jQuery };` ) ;
247+ return ;
248+ }
190249 if ( url . pathname === '/dynamic-script.js' ) {
191250 res . writeHead ( 200 , { 'Content-Type' : 'text/javascript; charset=utf-8' , 'Cache-Control' : 'no-store' } ) ;
192251 res . end ( `window.__dynamicScriptLoaded = {
@@ -580,6 +639,37 @@ test('browser traffic uses internal SOCKS5 mode and covers proxied runtime integ
580639 assert . ok ( requests . some ( r => r . url . startsWith ( '/module-worker.js' ) && r . userAgent === TARGET_UA ) , `target requests: ${ JSON . stringify ( requests ) } ` ) ;
581640 assert . ok ( requests . some ( r => r . url . startsWith ( '/worker-fixture.js' ) && r . userAgent === TARGET_UA ) , `target requests: ${ JSON . stringify ( requests ) } ` ) ;
582641
642+ try {
643+ await page . waitForFunction ( ( ) => window . __jqueryFixture && window . __jqueryFixture . ready , { timeout : 30000 } ) ;
644+ } catch ( err ) {
645+ const state = await page . evaluate ( ( ) => ( {
646+ jquery : window . __jqueryFixture || null ,
647+ plugin : window . __jqueryPlugin || null ,
648+ hasJQuery : Boolean ( window . jQuery ) ,
649+ scripts : Array . from ( document . scripts ) . map ( s => ( { id : s . id , src : s . attributes . getNamedItem ( 'src' ) ?. value || '' , type : s . type || '' , blocked : s . hasAttribute ( 'data-zp-blocked-script' ) } ) ) ,
650+ } ) ) ;
651+ throw new Error ( `${ err . message } ; jquery state=${ JSON . stringify ( state ) } ; requests=${ JSON . stringify ( requests ) } ` ) ;
652+ }
653+ const jquery = await page . evaluate ( ( ) => window . __jqueryFixture ) ;
654+ assert . match ( jquery . version , / ^ 3 \. / ) ;
655+ assert . equal ( jquery . selectorText , 'one,two' ) ;
656+ assert . equal ( jquery . endMatchesRoot , true ) ;
657+ assert . equal ( jquery . delegated , 1 ) ;
658+ assert . equal ( jquery . dataClicked , true ) ;
659+ assert . equal ( jquery . attrClicked , 'yes' ) ;
660+ assert . equal ( jquery . parsedText , 'parsed' ) ;
661+ assert . equal ( jquery . param , 'a=1&b%5B%5D=x&b%5B%5D=y' ) ;
662+ assert . deepEqual ( jquery . ajaxData , { ok : true , path : '/jquery-ajax.json' } ) ;
663+ assert . equal ( jquery . plugin && jquery . plugin . loaded , true ) ;
664+ assert . equal ( jquery . plugin && jquery . plugin . jquery , true ) ;
665+ assert . ok ( jquery . plugin . href . startsWith ( `http://${ targetHost } :${ targetPort } /` ) , jquery . plugin . href ) ;
666+ assert . ok ( jquery . globalEvalHref . startsWith ( `http://${ targetHost } :${ targetPort } /` ) , jquery . globalEvalHref ) ;
667+ assert . ok ( jquery . locationHref . startsWith ( `http://${ targetHost } :${ targetPort } /` ) , jquery . locationHref ) ;
668+ assert . ok ( requests . some ( r => r . url . startsWith ( '/jquery.js' ) && r . userAgent === TARGET_UA ) , `target requests: ${ JSON . stringify ( requests ) } ` ) ;
669+ assert . ok ( requests . some ( r => r . url . startsWith ( '/jquery-fixture.js' ) && r . userAgent === TARGET_UA ) , `target requests: ${ JSON . stringify ( requests ) } ` ) ;
670+ assert . ok ( requests . some ( r => r . url . startsWith ( '/jquery-ajax.json' ) && r . userAgent === TARGET_UA ) , `target requests: ${ JSON . stringify ( requests ) } ` ) ;
671+ assert . ok ( requests . some ( r => r . url . startsWith ( '/jquery-plugin.js' ) && r . userAgent === TARGET_UA ) , `target requests: ${ JSON . stringify ( requests ) } ` ) ;
672+
583673 const iframeIsolation = await page . evaluate ( async target => {
584674 const blockedByPolicy = fn => {
585675 try { fn ( ) ; return '' ; }
0 commit comments