1. /**
    
  2.  * Copyright (c) Meta Platforms, Inc. and affiliates.
    
  3.  *
    
  4.  * This source code is licensed under the MIT license found in the
    
  5.  * LICENSE file in the root directory of this source tree.
    
  6.  *
    
  7.  * @emails react-core
    
  8.  */
    
  9. 
    
  10. 'use strict';
    
  11. 
    
  12. import {insertNodesAndExecuteScripts} from 'react-dom/src/test-utils/FizzTestUtils';
    
  13. 
    
  14. // Polyfills for test environment
    
  15. global.ReadableStream =
    
  16.   require('web-streams-polyfill/ponyfill/es6').ReadableStream;
    
  17. global.TextEncoder = require('util').TextEncoder;
    
  18. global.TextDecoder = require('util').TextDecoder;
    
  19. 
    
  20. // Don't wait before processing work on the server.
    
  21. // TODO: we can replace this with FlightServer.act().
    
  22. global.setTimeout = cb => cb();
    
  23. 
    
  24. let container;
    
  25. let serverExports;
    
  26. let turbopackServerMap;
    
  27. let React;
    
  28. let ReactDOMServer;
    
  29. let ReactServerDOMServer;
    
  30. let ReactServerDOMClient;
    
  31. 
    
  32. describe('ReactFlightDOMForm', () => {
    
  33.   beforeEach(() => {
    
  34.     jest.resetModules();
    
  35.     // Simulate the condition resolution
    
  36.     jest.mock('react', () => require('react/react.shared-subset'));
    
  37.     jest.mock('react-server-dom-turbopack/server', () =>
    
  38.       require('react-server-dom-turbopack/server.edge'),
    
  39.     );
    
  40.     ReactServerDOMServer = require('react-server-dom-turbopack/server.edge');
    
  41.     const TurbopackMock = require('./utils/TurbopackMock');
    
  42.     serverExports = TurbopackMock.serverExports;
    
  43.     turbopackServerMap = TurbopackMock.turbopackServerMap;
    
  44.     __unmockReact();
    
  45.     jest.resetModules();
    
  46.     React = require('react');
    
  47.     ReactServerDOMClient = require('react-server-dom-turbopack/client.edge');
    
  48.     ReactDOMServer = require('react-dom/server.edge');
    
  49.     container = document.createElement('div');
    
  50.     document.body.appendChild(container);
    
  51.   });
    
  52. 
    
  53.   afterEach(() => {
    
  54.     document.body.removeChild(container);
    
  55.   });
    
  56. 
    
  57.   async function POST(formData) {
    
  58.     const boundAction = await ReactServerDOMServer.decodeAction(
    
  59.       formData,
    
  60.       turbopackServerMap,
    
  61.     );
    
  62.     return boundAction();
    
  63.   }
    
  64. 
    
  65.   function submit(submitter) {
    
  66.     const form = submitter.form || submitter;
    
  67.     if (!submitter.form) {
    
  68.       submitter = undefined;
    
  69.     }
    
  70.     const submitEvent = new Event('submit', {bubbles: true, cancelable: true});
    
  71.     submitEvent.submitter = submitter;
    
  72.     const returnValue = form.dispatchEvent(submitEvent);
    
  73.     if (!returnValue) {
    
  74.       return;
    
  75.     }
    
  76.     const action =
    
  77.       (submitter && submitter.getAttribute('formaction')) || form.action;
    
  78.     if (!/\s*javascript:/i.test(action)) {
    
  79.       const method = (submitter && submitter.formMethod) || form.method;
    
  80.       const encType = (submitter && submitter.formEnctype) || form.enctype;
    
  81.       if (method === 'post' && encType === 'multipart/form-data') {
    
  82.         let formData;
    
  83.         if (submitter) {
    
  84.           const temp = document.createElement('input');
    
  85.           temp.name = submitter.name;
    
  86.           temp.value = submitter.value;
    
  87.           submitter.parentNode.insertBefore(temp, submitter);
    
  88.           formData = new FormData(form);
    
  89.           temp.parentNode.removeChild(temp);
    
  90.         } else {
    
  91.           formData = new FormData(form);
    
  92.         }
    
  93.         return POST(formData);
    
  94.       }
    
  95.       throw new Error('Navigate to: ' + action);
    
  96.     }
    
  97.   }
    
  98. 
    
  99.   async function readIntoContainer(stream) {
    
  100.     const reader = stream.getReader();
    
  101.     let result = '';
    
  102.     while (true) {
    
  103.       const {done, value} = await reader.read();
    
  104.       if (done) {
    
  105.         break;
    
  106.       }
    
  107.       result += Buffer.from(value).toString('utf8');
    
  108.     }
    
  109.     const temp = document.createElement('div');
    
  110.     temp.innerHTML = result;
    
  111.     insertNodesAndExecuteScripts(temp, container, null);
    
  112.   }
    
  113. 
    
  114.   // @gate enableFormActions
    
  115.   it('can submit a passed server action without hydrating it', async () => {
    
  116.     let foo = null;
    
  117. 
    
  118.     const serverAction = serverExports(function action(formData) {
    
  119.       foo = formData.get('foo');
    
  120.       return 'hello';
    
  121.     });
    
  122.     function App() {
    
  123.       return (
    
  124.         <form action={serverAction}>
    
  125.           <input type="text" name="foo" defaultValue="bar" />
    
  126.         </form>
    
  127.       );
    
  128.     }
    
  129.     const rscStream = ReactServerDOMServer.renderToReadableStream(<App />);
    
  130.     const response = ReactServerDOMClient.createFromReadableStream(rscStream, {
    
  131.       ssrManifest: {
    
  132.         moduleMap: null,
    
  133.         moduleLoading: null,
    
  134.       },
    
  135.     });
    
  136.     const ssrStream = await ReactDOMServer.renderToReadableStream(response);
    
  137.     await readIntoContainer(ssrStream);
    
  138. 
    
  139.     const form = container.firstChild;
    
  140. 
    
  141.     expect(foo).toBe(null);
    
  142. 
    
  143.     const result = await submit(form);
    
  144. 
    
  145.     expect(result).toBe('hello');
    
  146.     expect(foo).toBe('bar');
    
  147.   });
    
  148. });