Skip to content

Commit 1c07e90

Browse files
committed
feat: add jQuery as a dependency and implement basic jQuery fixture for testing
1 parent acc929c commit 1c07e90

3 files changed

Lines changed: 99 additions & 0 deletions

File tree

package-lock.json

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
},
1616
"devDependencies": {
1717
"esbuild": "^0.28.0",
18+
"jquery": "^3.7.1",
1819
"puppeteer": "^25.0.4"
1920
}
2021
}

test/e2e/proxy.test.js

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const path = require('node:path');
1010
const puppeteer = require('puppeteer');
1111

1212
const 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

1415
function 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

Comments
 (0)