@@ -47,6 +47,7 @@ module DummyContext = {
47
47
48
48
[@ mel . get ] external tagName : Dom . element => string = "tagName" ;
49
49
[@ mel . get ] external innerHTML : Dom . element => string = "innerHTML" ;
50
+ [@ mel . set ] external setInnerHTML : (Dom . element , string ) => unit = "innerHTML" ;
50
51
51
52
let getByRole = (role, container) => {
52
53
ReactTestingLibrary . getByRole(~matcher= ` Str (role), container);
@@ -62,6 +63,24 @@ let getByTag = (tag, container) => {
62
63
[@ mel . send ]
63
64
external getAttribute : (Dom . element , string ) => option (string ) =
64
65
"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" ;
65
84
66
85
describe("React" , () => {
67
86
test("can render DOM elements" , () => {
@@ -233,37 +252,120 @@ describe("React", () => {
233
252
expect(image-> getAttribute("src" ))-> toEqual(Some ("https://foo.png" ));
234
253
});
235
254
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
+ React . useEffect1(
261
+ () => {
262
+ document-> setTitle(
263
+ "You clicked " ++ Int . to_string(count) ++ " times" ,
264
+ );
265
+ None ;
266
+ },
267
+ [| count|] ,
268
+ );
269
+
270
+ <div >
271
+ <button
272
+ className= "Increment" onClick= {_ => setCount(. prev => prev + 1 )}>
273
+ {React . string("Increment" )}
274
+ </button >
275
+ <span className= "Value" > {React . string(string_of_int(count))} </span >
276
+ </div >;
249
277
};
278
+ };
250
279
251
- let containerRef : ref (option (ReactTestingLibrary . renderResult )) =
252
- ref (None );
253
-
254
- React . act(() => {
255
- let container = ReactTestingLibrary . render(<Counter />);
256
- let button = getByRole("Increment" , container);
257
- FireEvent . click(button);
258
- containerRef. contents = Some (container);
259
- Js . Promise . resolve() ;
260
- });
261
-
262
- switch (containerRef. contents) {
263
- | Some (container ) =>
264
- expect(getByRole("counter" , container)-> innerHTML)-> toBe("1" )
265
- | None => failwith ("Container is null" )
280
+ testPromise("act" , finish => {
281
+ /* This test doesn't use ReactTestingLibrary to test the act API, and the code comes from
282
+ https://react.dev/reference/react/act example */
283
+
284
+ let container : Dom . element = createElement("div" );
285
+ body-> appendChild(container);
286
+
287
+ let .await () =
288
+ React . act (() => {
289
+ let root = ReactDOM.Client.createRoot(container);
290
+ ReactDOM.Client.render(root, <Counter />);
291
+ });
292
+
293
+ let valueElement = querySelector(".Value" , container);
294
+ switch (valueElement) {
295
+ | Some (value ) => expect(value-> innerHTML)-> toBe("0" )
296
+ | None => failwith ("Can't find 'Value' element" )
297
+ };
298
+
299
+ let title = getTitle(document);
300
+ expect(title)-> toBe("You clicked 0 times" );
301
+
302
+ let .await () =
303
+ React . act (() => {
304
+ let buttonElement = querySelector(".Increment", container);
305
+ switch (buttonElement) {
306
+ | Some(button) =>
307
+ dispatchEvent(button, mouseEvent("click", {"bubbles": true}))
308
+ | None => failwith ("Can't find 'Increment' button" )
309
+ };
310
+ });
311
+
312
+ let valueElement = querySelector(".Value" , container);
313
+ switch (valueElement) {
314
+ | Some (value ) => expect(value-> innerHTML)-> toBe("1" )
315
+ | None => failwith ("Can't find 'Value' element" )
316
+ };
317
+
318
+ let title = getTitle(document);
319
+ expect(title)-> toBe("You clicked 1 times" );
320
+
321
+ finish() ;
322
+ });
323
+
324
+ testPromise("actAsync" , finish => {
325
+ /* This test doesn't use ReactTestingLibrary to test the act API, and the code comes from
326
+ https://react.dev/reference/react/act example */
327
+
328
+ body-> setInnerHTML("" );
329
+ let container : Dom . element = createElement("div" );
330
+ body-> appendChild(container);
331
+
332
+ let .await () =
333
+ React . actAsync (() => {
334
+ let root = ReactDOM.Client.createRoot(container);
335
+ ReactDOM.Client.render(root, <Counter />);
336
+ Js . Promise . resolve();
337
+ });
338
+
339
+ let valueElement = querySelector(".Value" , container);
340
+ switch (valueElement) {
341
+ | Some (value ) => expect(value-> innerHTML)-> toBe("0" )
342
+ | None => failwith ("Can't find 'Value' element" )
266
343
};
344
+
345
+ let title = getTitle(document);
346
+ expect(title)-> toBe("You clicked 0 times" );
347
+
348
+ let .await () =
349
+ React . actAsync (() => {
350
+ let buttonElement = querySelector(".Increment", container);
351
+ switch (buttonElement) {
352
+ | Some(button) =>
353
+ dispatchEvent(button, mouseEvent("click", {"bubbles": true}))
354
+ | None => failwith ("Can't find 'Increment' button" )
355
+ };
356
+ Js . Promise . resolve() ;
357
+ });
358
+
359
+ let valueElement = querySelector(".Value" , container);
360
+ switch (valueElement) {
361
+ | Some (value ) => expect(value-> innerHTML)-> toBe("1" )
362
+ | None => failwith ("Can't find 'Value' element" )
363
+ };
364
+
365
+ let title = getTitle(document);
366
+ expect(title)-> toBe("You clicked 1 times" );
367
+
368
+ finish() ;
267
369
});
268
370
269
371
test("ErrorBoundary + Suspense" , () => {
0 commit comments