LCOV - code coverage report
Current view: top level - test/wpt/xhr - send-redirect-post-upload.js (source / functions) Hit Total Coverage
Test: lcov.info Lines: 283 287 98.6 %
Date: 2024-12-07 00:20:21 Functions: 9 9 100.0 %
Branches: 17 19 89.5 %

           Branch data     Line data    Source code
       1            [ + ]:          2 : import assert from 'node:assert/strict';
       2                 :          2 : import { XMLHttpRequest } from '../../../lib/whatwg-xhr.js';
       3                 :          2 : 
       4            [ + ]:          2 : export default (activeURL) => {
       5                 :          1 : 
       6                 :          1 :         const stringBody = 'Test Message'.repeat(1000);
       7                 :          1 :         const blobBody = new Blob(new Array(1000).fill('Test Message'));
       8                 :          1 : 
       9                 :          1 :         testRedirectPost( { name: '301',          code: 301, expectResendPost: false, body: stringBody} );
      10                 :          1 :         testRedirectPost( { name: '302',          code: 302, expectResendPost: false, body: stringBody} );
      11                 :          1 :         testRedirectPost( { name: '303',          code: 303, expectResendPost: false, body: stringBody} );
      12                 :          1 :         testRedirectPost( { name: '307 (string)', code: 307, expectResendPost: true,  body: stringBody, expectedBody: stringBody } );
      13                 :          1 :         testRedirectPost( { name: '307 (blob)',   code: 307, expectResendPost: true,  body: blobBody,   expectedBody: stringBody, expectedContentType: 'NO' } );
      14                 :          1 : 
      15            [ + ]:          1 :         function testRedirectPost(params){
      16                 :          5 :                 let actual = [];
      17                 :          5 :                 // We check upload.onprogress with a boolean because it *might* fire more than once
      18                 :          5 :                 let progressFiredReadyState1 = false;
      19                 :          5 : 
      20                 :          5 :                 let expectedHeaders, expectedEvents;
      21                 :          5 : 
      22                 :          5 :                 // 307 redirects should resend the POST data, and events and headers will be a little different..
      23            [ + ]:          5 :                 if(params.expectResendPost){
      24                 :          2 :                         expectedHeaders = {
      25                 :          2 :                                 'X-Request-Content-Length': '12000',
      26                 :          2 :                                 'X-Request-Content-Type': 'text/plain;charset=UTF-8',
      27                 :          2 :                                 'X-Request-Method': 'POST',
      28                 :          2 :                                 'X-Request-Query': 'NO',
      29                 :          2 :                                 'Content-Length': '12000'
      30                 :          2 :                         };
      31                 :          2 : 
      32                 :          2 :                         expectedEvents = [
      33                 :          2 :                                 'xhr onreadystatechange 1',
      34                 :          2 :                                 'xhr loadstart 1',
      35                 :          2 :                                 'upload loadstart 1',
      36                 :          2 :                                 'upload loadend 1',
      37                 :          2 :                                 'xhr onreadystatechange 2',
      38                 :          2 :                                 'xhr onreadystatechange 3',
      39                 :          2 :                                 'xhr onreadystatechange 4',
      40                 :          2 :                                 'xhr load 4',
      41                 :          2 :                                 'xhr loadend 4'
      42                 :          2 :                         ];
      43            [ + ]:          2 :                 }
      44                 :          3 :                 else {
      45                 :          3 :                         // setting the right expectations for POST resent as GET without request body
      46                 :          3 :                         expectedHeaders = {
      47                 :          3 :                                 'X-Request-Content-Length': 'NO',
      48                 :          3 :                                 'X-Request-Content-Type': 'NO',
      49                 :          3 :                                 'X-Request-Method': 'GET',
      50                 :          3 :                                 'X-Request-Query': 'NO'
      51                 :          3 :                         };
      52                 :          3 : 
      53                 :          3 :                         expectedEvents = [
      54                 :          3 :                                 'xhr onreadystatechange 1',
      55                 :          3 :                                 'xhr loadstart 1',
      56                 :          3 :                                 'upload loadstart 1',
      57                 :          3 :                                 'upload loadend 1',
      58                 :          3 :                                 'xhr onreadystatechange 2',
      59                 :          3 :                                 // we expect no onreadystatechange readyState=3 event because there is no loading content
      60                 :          3 :                                 'xhr onreadystatechange 4',
      61                 :          3 :                                 'xhr load 4',
      62                 :          3 :                                 'xhr loadend 4'
      63                 :          3 :                         ];
      64                 :          3 :                 }
      65                 :          5 :                 // Override expectations if provided.
      66                 :          5 :                 if(params.expectedContentType)
      67            [ + ]:          5 :                         expectedHeaders['X-Request-Content-Type'] = params.expectedContentType;
      68                 :          5 : 
      69                 :          5 :                 let xhr = new XMLHttpRequest();
      70                 :          5 : 
      71            [ + ]:          5 :                 xhr.upload.onloadstart = (e) => {
      72                 :          5 :                         actual.push('upload loadstart ' + xhr.readyState);
      73                 :          5 :                 };
      74                 :          5 : 
      75            [ + ]:          5 :                 xhr.upload.onprogress = (e) => {
      76                 :          5 :                         // events every 50ms, one final when uploading is done
      77            [ - ]:          5 :                         if(xhr.readyState >= xhr.HEADERS_RECEIVED) {
      78                 :          0 :                                 assert.strictEqual(xhr.status, 200, 'JS never gets to see the 30x status code');
      79                 :          0 :                         }
      80                 :          5 :                         progressFiredReadyState1 = xhr.readyState === xhr.OPENED;
      81                 :          5 :                 };
      82                 :          5 : 
      83            [ + ]:          5 :                 xhr.upload.onloadend = () => {
      84                 :          5 :                         actual.push('upload loadend ' + xhr.readyState);
      85                 :          5 :                 };
      86                 :          5 : 
      87            [ + ]:          5 :                 xhr.onloadstart = () => {
      88                 :          5 :                         actual.push('xhr loadstart ' + xhr.readyState);
      89                 :          5 :                 };
      90                 :          5 : 
      91            [ + ]:          5 :                 xhr.onreadystatechange = () => {
      92            [ + ]:         17 :                         if(xhr.readyState >= xhr.HEADERS_RECEIVED) {
      93                 :         12 :                                 assert.strictEqual(xhr.status, 200, 'JS never gets to see the 30x status code');
      94                 :         12 :                         }
      95                 :         17 : 
      96                 :         17 :                         // The UA may fire multiple 'readystatechange' events while in
      97                 :         17 :                         // the 'loading' state.
      98                 :         17 :                         // https://xhr.spec.whatwg.org/#the-send()-method
      99       [ + ][ - ]:         17 :                         if(xhr.readyState === 3 && actual[actual.length - 1] === 'xhr onreadystatechange 3') {
     100                 :          0 :                                 return;
     101                 :          0 :                         }
     102                 :         17 : 
     103                 :         17 :                         actual.push('xhr onreadystatechange ' + xhr.readyState);
     104                 :          5 :                 };
     105                 :          5 : 
     106            [ + ]:          5 :                 xhr.onload = (e) => {
     107                 :          5 :                         actual.push('xhr load ' + xhr.readyState);
     108                 :          5 :                 };
     109                 :          5 : 
     110            [ + ]:          5 :                 xhr.onloadend = (e) => {
     111                 :          5 :                         actual.push('xhr loadend ' + xhr.readyState);
     112                 :          5 : 
     113                 :          5 :                         assert(progressFiredReadyState1, 'One progress event should fire on xhr.upload when readyState is 1');
     114                 :          5 : 
     115                 :          5 :                         // Headers will tell us if data was sent when expected
     116            [ + ]:          5 :                         for(let header in expectedHeaders){
     117                 :         22 :                                 assert.strictEqual(xhr.getResponseHeader(header), expectedHeaders[header], header);
     118                 :         22 :                         }
     119                 :          5 : 
     120                 :          5 :                         assert.deepStrictEqual(actual, expectedEvents, 'events firing in expected order and states');
     121                 :          5 : 
     122                 :          5 :                         if(params.expectedBody)
     123            [ + ]:          5 :                                 assert.strictEqual(xhr.response, params.expectedBody, 'request body was resent');
     124                 :          5 : 
     125                 :          5 :                         xhr.upload.onloadstart = null;
     126                 :          5 :                         xhr.upload.onprogress  = null;
     127                 :          5 :                         xhr.upload.onloadend   = null;
     128                 :          5 : 
     129                 :          5 :                         xhr.onloadstart = null;
     130                 :          5 :                         xhr.onprogress  = null;
     131                 :          5 :                         xhr.onload      = null;
     132                 :          5 :                         xhr.onloadend   = null;
     133                 :          5 :                 };
     134                 :          5 : 
     135                 :          5 :                 xhr.open('POST', `${activeURL}/redirect.py?location=/xhr/resources/content.py&code=${params.code}`);
     136                 :          5 :                 xhr.send(params.body);
     137                 :          5 :         }
     138                 :          1 : }
     139                 :          2 : 
     140                 :          2 : /*
     141                 :          2 :  * send-redirect-post-upload.htm
     142                 :          2 :  *
     143                 :          2 : 
     144                 :          2 : <!DOCTYPE html>
     145                 :          2 : <html>
     146                 :          2 : <head>
     147                 :          2 :     <link rel="help" href="https://xhr.spec.whatwg.org/#handler-xhr-onprogress" data-tested-assertations="../.." />
     148                 :          2 :     <link rel="help" href="https://xhr.spec.whatwg.org/#event-xhr-progress" data-tested-assertations="../.." />
     149                 :          2 :     <link rel="help" href="https://xhr.spec.whatwg.org/#the-send()-method" data-tested-assertations="following::dt[@id="dom-xmlhttprequest-send-bodyinit"]/following::dd[1]/p[2] following::ol[1]/li[9]//li[1] following::ol[1]/li[9]//li[2]" />
     150                 :          2 :     <link rel="help" href="https://fetch.spec.whatwg.org/#http-fetch" data-tested-assertations="following::ol[1]/li[6]/dl/dd[1]//dd[3]" />
     151                 :          2 :     <link rel="help" href="https://fetch.spec.whatwg.org/#concept-http-redirect-fetch" data-tested-assertations="following::li[16]" />
     152                 :          2 :     <script src="/resources/testharness.js"></script>
     153                 :          2 :     <script src="/resources/testharnessreport.js"></script>
     154                 :          2 :     <title>XMLHttpRequest: The send() method: POSTing to URL that redirects</title>
     155                 :          2 : </head>
     156                 :          2 : 
     157                 :          2 : <body>
     158                 :          2 :     <div id="log"></div>
     159                 :          2 : 
     160                 :          2 :     <script type="text/javascript">
     161                 :          2 :     function testRedirectPost(params) {
     162                 :          2 :         var test = async_test(document.title + " (" + params.name + ")");
     163                 :          2 :         var actual = [];
     164                 :          2 :         // We check upload.onprogress with a boolean because it *might* fire more than once
     165                 :          2 :         var progressFiredReadyState1 = false;
     166                 :          2 : 
     167                 :          2 :         var expectedHeaders, expectedEvents;
     168                 :          2 : 
     169                 :          2 :         // 307 redirects should resend the POST data, and events and headers will be a little different..
     170                 :          2 :         if(params.expectResendPost) {
     171                 :          2 :             expectedHeaders = {
     172                 :          2 :                 "X-Request-Content-Length": "12000",
     173                 :          2 :                 "X-Request-Content-Type": "text/plain;charset=UTF-8",
     174                 :          2 :                 "X-Request-Method": "POST",
     175                 :          2 :                 "X-Request-Query": "NO",
     176                 :          2 :                 "Content-Length": "12000"
     177                 :          2 :             }
     178                 :          2 :             expectedEvents = [
     179                 :          2 :                 "xhr onreadystatechange 1",
     180                 :          2 :                 "xhr loadstart 1",
     181                 :          2 :                 "upload loadstart 1",
     182                 :          2 :                 "upload loadend 1",
     183                 :          2 :                 "xhr onreadystatechange 2",
     184                 :          2 :                 "xhr onreadystatechange 3",
     185                 :          2 :                 "xhr onreadystatechange 4",
     186                 :          2 :                 "xhr load 4",
     187                 :          2 :                 "xhr loadend 4"
     188                 :          2 :             ];
     189                 :          2 :         } else {
     190                 :          2 :             // setting the right expectations for POST resent as GET without request body
     191                 :          2 :             expectedHeaders = {
     192                 :          2 :                 "X-Request-Content-Length": "NO",
     193                 :          2 :                 "X-Request-Content-Type": "NO",
     194                 :          2 :                 "X-Request-Method": "GET",
     195                 :          2 :                 "X-Request-Query": "NO"
     196                 :          2 :             }
     197                 :          2 :             expectedEvents = [
     198                 :          2 :                 "xhr onreadystatechange 1",
     199                 :          2 :                 "xhr loadstart 1",
     200                 :          2 :                 "upload loadstart 1",
     201                 :          2 :                 "upload loadend 1",
     202                 :          2 :                 "xhr onreadystatechange 2",
     203                 :          2 :                 // we expect no onreadystatechange readyState=3 event because there is no loading content
     204                 :          2 :                 "xhr onreadystatechange 4",
     205                 :          2 :                 "xhr load 4",
     206                 :          2 :                 "xhr loadend 4"
     207                 :          2 :             ];
     208                 :          2 :         }
     209                 :          2 :         // Override expectations if provided.
     210                 :          2 :         if(params.expectedContentType)
     211                 :          2 :           expectedHeaders["X-Request-Content-Type"] = params.expectedContentType;
     212                 :          2 : 
     213                 :          2 :         test.step(function()
     214                 :          2 :         {
     215                 :          2 :             var xhr = new XMLHttpRequest();
     216                 :          2 : 
     217                 :          2 :             xhr.upload.onloadstart = test.step_func(function(e) {
     218                 :          2 :                 actual.push("upload loadstart " + xhr.readyState);
     219                 :          2 :             });
     220                 :          2 :             xhr.upload.onprogress = test.step_func(function(e) {
     221                 :          2 :                 // events every 50ms, one final when uploading is done
     222                 :          2 :                 if(xhr.readyState >= xhr.HEADERS_RECEIVED) {
     223                 :          2 :                     assert_equals(xhr.status, 200, "JS never gets to see the 30x status code");
     224                 :          2 :                 }
     225                 :          2 :                 progressFiredReadyState1 = xhr.readyState === xhr.OPENED;
     226                 :          2 :             });
     227                 :          2 :             xhr.upload.onloadend = test.step_func(function() {
     228                 :          2 :                 actual.push("upload loadend " + xhr.readyState);
     229                 :          2 :             });
     230                 :          2 :             xhr.onloadstart = test.step_func(function() {
     231                 :          2 :                 actual.push("xhr loadstart " + xhr.readyState);
     232                 :          2 :             });
     233                 :          2 :             xhr.onreadystatechange = test.step_func(function() {
     234                 :          2 :                 if(xhr.readyState >= xhr.HEADERS_RECEIVED) {
     235                 :          2 :                     assert_equals(xhr.status, 200, "JS never gets to see the 30x status code");
     236                 :          2 :                 }
     237                 :          2 : 
     238                 :          2 :                 // The UA may fire multiple "readystatechange" events while in
     239                 :          2 :                 // the "loading" state.
     240                 :          2 :                 // https://xhr.spec.whatwg.org/#the-send()-method
     241                 :          2 :                 if (xhr.readyState === 3 && actual[actual.length - 1] === "xhr onreadystatechange 3") {
     242                 :          2 :                     return;
     243                 :          2 :                 }
     244                 :          2 : 
     245                 :          2 :                 actual.push("xhr onreadystatechange " + xhr.readyState);
     246                 :          2 :             });
     247                 :          2 :             xhr.onload = test.step_func(function(e)
     248                 :          2 :             {
     249                 :          2 :                 actual.push("xhr load " + xhr.readyState);
     250                 :          2 :             });
     251                 :          2 :             xhr.onloadend = test.step_func(function(e)
     252                 :          2 :             {
     253                 :          2 :                 actual.push("xhr loadend " + xhr.readyState);
     254                 :          2 : 
     255                 :          2 :                 assert_true(progressFiredReadyState1, "One progress event should fire on xhr.upload when readyState is 1");
     256                 :          2 : 
     257                 :          2 :                 // Headers will tell us if data was sent when expected
     258                 :          2 :                 for(var header in expectedHeaders) {
     259                 :          2 :                     assert_equals(xhr.getResponseHeader(header), expectedHeaders[header], header);
     260                 :          2 :                 }
     261                 :          2 : 
     262                 :          2 :                 assert_array_equals(actual, expectedEvents, "events firing in expected order and states");
     263                 :          2 :                 if (params.expectedBody)
     264                 :          2 :                   assert_equals(xhr.response, params.expectedBody, 'request body was resent');
     265                 :          2 :                 test.done();
     266                 :          2 :             });
     267                 :          2 : 
     268                 :          2 :             xhr.open("POST", "./resources/redirect.py?location=content.py&code=" + params.code, true);
     269                 :          2 :             xhr.send(params.body);
     270                 :          2 :         });
     271                 :          2 :     }
     272                 :          2 : 
     273                 :          2 :     const stringBody = "Test Message".repeat(1000);
     274                 :          2 :     const blobBody = new Blob(new Array(1000).fill("Test Message"));
     275                 :          2 : 
     276                 :          2 :     testRedirectPost({name: "301", code: 301, expectResendPost: false, body: stringBody});
     277                 :          2 :     testRedirectPost({name: "302", code: 302, expectResendPost: false, body: stringBody});
     278                 :          2 :     testRedirectPost({name: "303", code: 303, expectResendPost: false, body: stringBody});
     279                 :          2 :     testRedirectPost({name: "307 (string)", code: 307, expectResendPost: true,  body: stringBody, expectedBody: stringBody });
     280                 :          2 :     testRedirectPost({name: "307 (blob)", code: 307, expectResendPost: true, body: blobBody, expectedBody: stringBody, expectedContentType: "NO" });
     281                 :          2 :     </script>
     282                 :          2 : </body>
     283                 :          2 : </html>
     284                 :          2 : 
     285                 :          2 :  *
     286                 :          2 :  * send-redirect-post-upload.htm
     287                 :          2 :  */

Generated by: LCOV version 1.14