@@ -47,6 +47,7 @@ module DummyContext = {
4747
4848[@ mel . get ] external tagName : Dom . element => string = "tagName" ;
4949[@ mel . get ] external innerHTML : Dom . element => string = "innerHTML" ;
50+ [@ mel . set ] external setInnerHTML : (Dom . element , string ) => unit = "innerHTML" ;
5051
5152let getByRole = (role, container) => {
5253 ReactTestingLibrary . getByRole(~matcher= ` Str (role), container);
@@ -62,6 +63,24 @@ let getByTag = (tag, container) => {
6263[@ mel . send ]
6364external getAttribute : (Dom . element , string ) => option (string ) =
6465 "getAttribute" ;
66+ [@ mel . set ] external setTitle : (Dom . element , string ) => unit = "title" ;
67+ [@ mel . get ] external getTitle : Dom . element => string = "title" ;
68+
69+ let (let .await ) = (p, f) => Js . Promise . then_(f, p);
70+
71+ external createElement : string => Dom . element = "document.createElement" ;
72+ [@ mel . send ]
73+ external appendChild : (Dom . element , Dom . element ) => unit = "appendChild" ;
74+ external document : Dom . element = "document" ;
75+ external body : Dom . element = "document.body" ;
76+ external querySelector : (string , Dom . element ) => option (Dom . element ) =
77+ "document.querySelector" ;
78+
79+ [@ mel . new ]
80+ external mouseEvent : (string , Js . t (' a )) => Dom . event = "MouseEvent" ;
81+
82+ [@ mel . send ]
83+ external dispatchEvent : (Dom . element , Dom . event ) => unit = "dispatchEvent" ;
6584
6685describe("React" , () => {
6786 test("can render DOM elements" , () => {
@@ -233,36 +252,129 @@ describe("React", () => {
233252 expect(image-> getAttribute("src" ))-> toEqual(Some ("https://foo.png" ));
234253 });
235254
236- test("React.act" , () => {
237- module Counter = {
238- [@ react . component ]
239- let make = () => {
240- let (count , setCount ) = React . useState(() => 0 );
241-
242- <div >
243- <button role= "Increment" onClick= {_ => setCount(prev => prev + 1 )}>
244- {React . string("Increment" )}
245- </button >
246- <span role= "counter" > {React . string(string_of_int(count))} </span >
247- </div >;
248- };
255+ module Counter = {
256+ [@ react . component ]
257+ let make = () => {
258+ let (count , setCount ) = React . Uncurried . useState(() => 0 );
259+
260+ <div >
261+ <button role= "Increment" onClick= {_ => setCount(. prev => prev + 1 )}>
262+ {React . string("Increment" )}
263+ </button >
264+ <span role= "counter" > {React . string(string_of_int(count))} </span >
265+ </div >;
249266 };
267+ };
250268
269+ testPromise("act" , finish => {
251270 let containerRef : ref (Js . nullable (ReactTestingLibrary . renderResult )) =
252271 ref (Js . Nullable . null);
253272
254- React . act(() => {
255- let container = ReactTestingLibrary . render(<Counter />);
256- let button = getByRole("Increment" , container);
257- FireEvent . click(button);
258- containerRef. contents = Js . Nullable . return(container);
259- });
273+ let .await _ =
274+ React . act (() => {
275+ let container = ReactTestingLibrary.render(<Counter />);
276+ let button = getByRole("Increment", container);
277+ FireEvent.click(button);
278+ containerRef.contents = Js.Nullable.return(container);
279+ });
260280
261281 switch (Js . Nullable . toOption(containerRef. contents)) {
262282 | Some (container ) =>
263283 expect(getByRole("counter" , container)-> innerHTML)-> toBe("1" )
264284 | None => failwith ("Container is null" )
265285 };
286+ finish() ;
287+ });
288+
289+ testPromise("act" , finish => {
290+ /* This test doesn't use ReactTestingLibrary to test the act API, and the code comes from
291+ https://react.dev/reference/react/act example */
292+
293+ let container : Dom . element = createElement("div" );
294+ body-> appendChild(container);
295+
296+ let .await () =
297+ React . act (() => {
298+ let root = ReactDOM.Client.createRoot(container);
299+ ReactDOM.Client.render(root, <Counter />);
300+ });
301+
302+ let valueElement = querySelector(".Value" , container);
303+ switch (valueElement) {
304+ | Some (value ) => expect(value-> innerHTML)-> toBe("0" )
305+ | None => failwith ("Can't find 'Value' element" )
306+ };
307+
308+ let title = getTitle(document);
309+ expect(title)-> toBe("You clicked 0 times" );
310+
311+ let .await () =
312+ React . act (() => {
313+ let buttonElement = querySelector(".Increment", container);
314+ switch (buttonElement) {
315+ | Some(button) =>
316+ dispatchEvent(button, mouseEvent("click", {"bubbles": true}))
317+ | None => failwith ("Can't find 'Increment' button" )
318+ };
319+ });
320+
321+ let valueElement = querySelector(".Value" , container);
322+ switch (valueElement) {
323+ | Some (value ) => expect(value-> innerHTML)-> toBe("1" )
324+ | None => failwith ("Can't find 'Value' element" )
325+ };
326+
327+ let title = getTitle(document);
328+ expect(title)-> toBe("You clicked 1 times" );
329+
330+ finish() ;
331+ });
332+
333+ testPromise("actAsync" , finish => {
334+ /* This test doesn't use ReactTestingLibrary to test the act API, and the code comes from
335+ https://react.dev/reference/react/act example */
336+
337+ body-> setInnerHTML("" );
338+ let container : Dom . element = createElement("div" );
339+ body-> appendChild(container);
340+
341+ let .await () =
342+ React . actAsync (() => {
343+ let root = ReactDOM.Client.createRoot(container);
344+ ReactDOM.Client.render(root, <Counter />);
345+ Js . Promise . resolve();
346+ });
347+
348+ let valueElement = querySelector(".Value" , container);
349+ switch (valueElement) {
350+ | Some (value ) => expect(value-> innerHTML)-> toBe("0" )
351+ | None => failwith ("Can't find 'Value' element" )
352+ };
353+
354+ let title = getTitle(document);
355+ expect(title)-> toBe("You clicked 0 times" );
356+
357+ let .await () =
358+ React . actAsync (() => {
359+ let buttonElement = querySelector(".Increment", container);
360+ switch (buttonElement) {
361+ | Some(button) =>
362+ dispatchEvent(button, mouseEvent("click", {"bubbles": true}))
363+ | None => failwith ("Can't find 'Increment' button" )
364+ };
365+ Js . Promise . resolve() ;
366+ });
367+
368+ let valueElement = querySelector(".Value" , container);
369+ switch (valueElement) {
370+ | Some (value ) => expect(value-> innerHTML)-> toBe("1" )
371+ | None => failwith ("Can't find 'Value' element" )
372+ };
373+
374+ let title = getTitle(document);
375+ expect(title)-> toBe("You clicked 1 times" );
376+
377+ finish() ;
266378 });
267379
268380 test("ErrorBoundary + Suspense" , () => {
0 commit comments