testcafe icon indicating copy to clipboard operation
testcafe copied to clipboard

Enable POST data body override on native automation

Open akcyp opened this issue 5 months ago • 1 comments

Purpose

Currently overriding POST data body is only possible when running tests with --disable-native-automation. Take a look at following example:

// tests/test.ts
import { RequestHook, Selector } from 'testcafe';
import { ResponseEvent, RequestEvent } from 'testcafe-hammerhead';

class TestHook extends RequestHook {
  constructor() {
    super([
      { url: /echo.free.beeceptor.com/, method: 'POST' },
    ], {
      includeBody: true,
      includeHeaders: true,
    });
  }

  async onRequest(requestEvent: RequestEvent & { requestOptions: any }) {
    const mockPostData = Buffer.from(JSON.stringify({ test: 'override' }));
    requestEvent.requestOptions.body = mockPostData;
    requestEvent.requestOptions.headers['content-length'] = mockPostData.length.toString();
  }
  
  async onResponse(responseEvent: ResponseEvent) {}
}

fixture `TestCafe API`
    .page `http://localhost:5500/`
    .requestHooks(new TestHook());

test('API', async t => {
  const content = await Selector('#output').innerText;
  await t.wait(500);
  await t.expect(content).eql('{"test":"override"}');
});
<!-- index.html served at localhost:5500 -->
<!DOCTYPE html>
<html lang="en">
<body>
  <div id="output"></div>
  <script>
      async function main() {
        const response = await fetch('https://echo.free.beeceptor.com', {
          method: 'POST',
          body: JSON.stringify({
            example: 'payload',
          }),
        });
        const output = await response.json();
        document.querySelector('#output').innerHTML = output.rawBody;
      }
      main();
  </script>
</body>
</html>

This PR will allow this value to be overridden in RequestHooks also when running tests on native automation.

Approach

Based on https://chromedevtools.github.io/devtools-protocol/tot/Fetch/#method-continueRequest we can achieve this feature by just adding postData encoded to base64 to contineRequest props. This is already done at _createContinueEventArgs to fix upload-like requests:

private _createContinueEventArgs (event: Protocol.Fetch.RequestPausedEvent, reqOpts: any): ContinueRequestArgs {
    const continueEventArgs = {
        postData: this._getUploadPostData(event),
    };

    if (reqOpts)
        Object.assign(continueEventArgs, this._getRequestOptionsModifiedByRequestHook(event, reqOpts));

    return continueEventArgs;
}

The only thing I've noticed that could be improved is that in the case of --disable-native-automation, we need to explicitly correct the content-length header. In the case of native automation, after implementing this feature, the user shouldn't change it, as it leads to query failures, so the example RequestHook would look like this:

class TestHook extends RequestHook {
  constructor() {
    super([
      { url: /echo.free.beeceptor.com/, method: 'POST' },
    ], {
      includeBody: true,
      includeHeaders: true,
    });
  }

  async onRequest(requestEvent: RequestEvent & { requestOptions: any }) {
    const mockResponse = Buffer.from(JSON.stringify({ test: 'override' }));
    requestEvent.requestOptions.body = mockResponse;
-    requestEvent.requestOptions.headers['content-length'] = mockResponse.length.toString();
  }

Pre-Merge TODO

  • [ ] Write tests for your proposed changes
  • [ ] Make sure that existing tests do not fail

akcyp avatar Jul 31 '25 12:07 akcyp

Thank you for your contribution to TestCafe. When a member of the TestCafe team becomes available, they will review this PR.

github-actions[bot] avatar Aug 05 '25 13:08 github-actions[bot]