@@ -278,6 +278,143 @@ async fn test_url_search_params_encoding() {
278278 . await ;
279279}
280280
281+ // ============================================================================
282+ // URLSearchParams constructor variants
283+ // ============================================================================
284+
285+ #[ tokio:: test]
286+ async fn test_url_search_params_from_object ( ) {
287+ run_local ( || async {
288+ let result = eval_js (
289+ r#"addEventListener('fetch', (event) => {
290+ const params = new URLSearchParams({
291+ grant_type: 'authorization_code',
292+ client_id: 'my-client',
293+ client_secret: 'my-secret',
294+ code: 'abc123'
295+ });
296+ event.respondWith(new Response(params.toString()));
297+ });"# ,
298+ )
299+ . await ;
300+
301+ assert_eq ! (
302+ result,
303+ "grant_type=authorization_code&client_id=my-client&client_secret=my-secret&code=abc123"
304+ ) ;
305+ } )
306+ . await ;
307+ }
308+
309+ #[ tokio:: test]
310+ async fn test_url_search_params_from_object_encodes_special_chars ( ) {
311+ run_local ( || async {
312+ let result = eval_js (
313+ r#"addEventListener('fetch', (event) => {
314+ const params = new URLSearchParams({
315+ redirect_uri: 'https://example.com/callback?foo=bar',
316+ scope: 'read write'
317+ });
318+ event.respondWith(new Response(params.toString()));
319+ });"# ,
320+ )
321+ . await ;
322+
323+ assert_eq ! (
324+ result,
325+ "redirect_uri=https%3A%2F%2Fexample.com%2Fcallback%3Ffoo%3Dbar&scope=read%20write"
326+ ) ;
327+ } )
328+ . await ;
329+ }
330+
331+ #[ tokio:: test]
332+ async fn test_url_search_params_from_array_of_pairs ( ) {
333+ run_local ( || async {
334+ let result = eval_js (
335+ r#"addEventListener('fetch', (event) => {
336+ const params = new URLSearchParams([['a', '1'], ['b', '2'], ['a', '3']]);
337+ const all = params.getAll('a');
338+ event.respondWith(new Response(params.toString() + '|' + all.join(',')));
339+ });"# ,
340+ )
341+ . await ;
342+
343+ assert_eq ! ( result, "a=1&b=2&a=3|1,3" ) ;
344+ } )
345+ . await ;
346+ }
347+
348+ #[ tokio:: test]
349+ async fn test_url_search_params_from_another_instance ( ) {
350+ run_local ( || async {
351+ let result = eval_js (
352+ r#"addEventListener('fetch', (event) => {
353+ const original = new URLSearchParams('x=1&y=2');
354+ const copy = new URLSearchParams(original);
355+ copy.set('y', '99');
356+ // original should be unaffected
357+ event.respondWith(new Response(original.toString() + '|' + copy.toString()));
358+ });"# ,
359+ )
360+ . await ;
361+
362+ assert_eq ! ( result, "x=1&y=2|x=1&y=99" ) ;
363+ } )
364+ . await ;
365+ }
366+
367+ #[ tokio:: test]
368+ async fn test_url_search_params_size ( ) {
369+ run_local ( || async {
370+ let result = eval_js (
371+ r#"addEventListener('fetch', (event) => {
372+ const empty = new URLSearchParams();
373+ const three = new URLSearchParams('a=1&b=2&c=3');
374+ event.respondWith(new Response(empty.size + ',' + three.size));
375+ });"# ,
376+ )
377+ . await ;
378+
379+ assert_eq ! ( result, "0,3" ) ;
380+ } )
381+ . await ;
382+ }
383+
384+ #[ tokio:: test]
385+ async fn test_url_search_params_as_fetch_body ( ) {
386+ run_local ( || async {
387+ let result = eval_js (
388+ r#"addEventListener('fetch', (event) => {
389+ // Simulate what the PlanetScale OAuth code does:
390+ // new URLSearchParams({...}) passed as body to fetch
391+ const params = new URLSearchParams({
392+ grant_type: 'authorization_code',
393+ client_id: 'abc',
394+ code: 'xyz'
395+ });
396+
397+ // fetch() calls body.toString() — verify it produces valid form data
398+ const body = params.toString();
399+ const reparsed = new URLSearchParams(body);
400+
401+ const ok = reparsed.get('grant_type') === 'authorization_code'
402+ && reparsed.get('client_id') === 'abc'
403+ && reparsed.get('code') === 'xyz';
404+
405+ event.respondWith(new Response(ok ? body : 'FAIL: ' + body));
406+ });"# ,
407+ )
408+ . await ;
409+
410+ assert_eq ! (
411+ result,
412+ "grant_type=authorization_code&client_id=abc&code=xyz"
413+ ) ;
414+ } )
415+ . await ;
416+ }
417+
281418// ============================================================================
282419// btoa / atob (binary strings, NOT UTF-8)
283420// ============================================================================
0 commit comments