44from servestatic import ServeStaticASGI
55
66import reactpy
7+ import reactpy .web .module
78from reactpy .executors .asgi .standalone import ReactPy
89from reactpy .testing import (
910 BackendFixture ,
1920
2021
2122async def test_that_js_module_unmount_is_called (display : DisplayFixture ):
22- SomeComponent = reactpy .web .export (
23- reactpy .web .module_from_file (
23+ SomeComponent = reactpy .web .module . _vdom_from_web_module (
24+ reactpy .web .module . _module_from_file (
2425 "set-flag-when-unmount-is-called" ,
2526 JS_FIXTURES_DIR / "set-flag-when-unmount-is-called.js" ,
2627 ),
@@ -52,8 +53,10 @@ def ShowCurrentComponent():
5253
5354
5455async def test_module_from_url (browser ):
55- SimpleButton = reactpy .web .export (
56- reactpy .web .module_from_url ("/static/simple-button.js" , resolve_exports = False ),
56+ SimpleButton = reactpy .web .module ._vdom_from_web_module (
57+ reactpy .web .module ._module_from_url (
58+ "/static/simple-button.js" , resolve_exports = False
59+ ),
5760 "SimpleButton" ,
5861 )
5962
@@ -72,8 +75,8 @@ def ShowSimpleButton():
7275
7376
7477async def test_module_from_file (display : DisplayFixture ):
75- SimpleButton = reactpy .web .export (
76- reactpy .web .module_from_file (
78+ SimpleButton = reactpy .web .module . _vdom_from_web_module (
79+ reactpy .web .module . _module_from_file (
7780 "simple-button" , JS_FIXTURES_DIR / "simple-button.js"
7881 ),
7982 "SimpleButton" ,
@@ -98,30 +101,30 @@ def test_module_from_file_source_conflict(tmp_path):
98101 first_file = tmp_path / "first.js"
99102
100103 with pytest .raises (FileNotFoundError , match = "does not exist" ):
101- reactpy .web .module_from_file ("temp" , first_file )
104+ reactpy .web .module . _module_from_file ("temp" , first_file )
102105
103106 first_file .touch ()
104107
105- reactpy .web .module_from_file ("temp" , first_file )
108+ reactpy .web .module . _module_from_file ("temp" , first_file )
106109
107110 second_file = tmp_path / "second.js"
108111 second_file .touch ()
109112
110113 # ok, same content
111- reactpy .web .module_from_file ("temp" , second_file )
114+ reactpy .web .module . _module_from_file ("temp" , second_file )
112115
113116 third_file = tmp_path / "third.js"
114117 third_file .write_text ("something-different" )
115118
116119 with assert_reactpy_did_log (r"Existing web module .* will be replaced with" ):
117- reactpy .web .module_from_file ("temp" , third_file )
120+ reactpy .web .module . _module_from_file ("temp" , third_file )
118121
119122
120123def test_web_module_from_file_symlink (tmp_path ):
121124 file = tmp_path / "temp.js"
122125 file .touch ()
123126
124- module = reactpy .web .module_from_file ("temp" , file , symlink = True )
127+ module = reactpy .web .module . _module_from_file ("temp" , file , symlink = True )
125128
126129 assert module .file .resolve ().read_text () == ""
127130
@@ -134,44 +137,44 @@ def test_web_module_from_file_symlink_twice(tmp_path):
134137 file_1 = tmp_path / "temp_1.js"
135138 file_1 .touch ()
136139
137- reactpy .web .module_from_file ("temp" , file_1 , symlink = True )
140+ reactpy .web .module . _module_from_file ("temp" , file_1 , symlink = True )
138141
139142 with assert_reactpy_did_not_log (r"Existing web module .* will be replaced with" ):
140- reactpy .web .module_from_file ("temp" , file_1 , symlink = True )
143+ reactpy .web .module . _module_from_file ("temp" , file_1 , symlink = True )
141144
142145 file_2 = tmp_path / "temp_2.js"
143146 file_2 .write_text ("something" )
144147
145148 with assert_reactpy_did_log (r"Existing web module .* will be replaced with" ):
146- reactpy .web .module_from_file ("temp" , file_2 , symlink = True )
149+ reactpy .web .module . _module_from_file ("temp" , file_2 , symlink = True )
147150
148151
149152def test_web_module_from_file_replace_existing (tmp_path ):
150153 file1 = tmp_path / "temp1.js"
151154 file1 .touch ()
152155
153- reactpy .web .module_from_file ("temp" , file1 )
156+ reactpy .web .module . _module_from_file ("temp" , file1 )
154157
155158 file2 = tmp_path / "temp2.js"
156159 file2 .write_text ("something" )
157160
158161 with assert_reactpy_did_log (r"Existing web module .* will be replaced with" ):
159- reactpy .web .module_from_file ("temp" , file2 )
162+ reactpy .web .module . _module_from_file ("temp" , file2 )
160163
161164
162165def test_module_missing_exports ():
163166 module = WebModule ("test" , NAME_SOURCE , None , {"a" , "b" , "c" }, None , False )
164167
165168 with pytest .raises (ValueError , match = "does not export 'x'" ):
166- reactpy .web .export (module , "x" )
169+ reactpy .web .module . _vdom_from_web_module (module , "x" )
167170
168171 with pytest .raises (ValueError , match = r"does not export \['x', 'y'\]" ):
169- reactpy .web .export (module , ["x" , "y" ])
172+ reactpy .web .module . _vdom_from_web_module (module , ["x" , "y" ])
170173
171174
172175async def test_module_exports_multiple_components (display : DisplayFixture ):
173- Header1 , Header2 = reactpy .web .export (
174- reactpy .web .module_from_file (
176+ Header1 , Header2 = reactpy .web .module . _vdom_from_web_module (
177+ reactpy .web .module . _module_from_file (
175178 "exports-two-components" , JS_FIXTURES_DIR / "exports-two-components.js"
176179 ),
177180 ["Header1" , "Header2" ],
@@ -187,10 +190,12 @@ async def test_module_exports_multiple_components(display: DisplayFixture):
187190
188191
189192async def test_imported_components_can_render_children (display : DisplayFixture ):
190- module = reactpy .web .module_from_file (
193+ module = reactpy .web .module . _module_from_file (
191194 "component-can-have-child" , JS_FIXTURES_DIR / "component-can-have-child.js"
192195 )
193- Parent , Child = reactpy .web .export (module , ["Parent" , "Child" ])
196+ Parent , Child = reactpy .web .module ._vdom_from_web_module (
197+ module , ["Parent" , "Child" ]
198+ )
194199
195200 await display .show (
196201 lambda : Parent (
@@ -219,10 +224,10 @@ async def test_keys_properly_propagated(display: DisplayFixture):
219224 This property is required for certain JS components, such as the GridLayout from
220225 react-grid-layout.
221226 """
222- module = reactpy .web .module_from_file (
227+ module = reactpy .web .module . _module_from_file (
223228 "keys-properly-propagated" , JS_FIXTURES_DIR / "keys-properly-propagated.js"
224229 )
225- GridLayout = reactpy .web .export (module , "GridLayout" )
230+ GridLayout = reactpy .web .module . _vdom_from_web_module (module , "GridLayout" )
226231
227232 await display .show (
228233 lambda : GridLayout (
@@ -273,12 +278,14 @@ async def test_keys_properly_propagated(display: DisplayFixture):
273278
274279
275280async def test_subcomponent_notation_as_str_attrs (display : DisplayFixture ):
276- module = reactpy .web .module_from_file (
281+ module = reactpy .web .module . _module_from_file (
277282 "subcomponent-notation" ,
278283 JS_FIXTURES_DIR / "subcomponent-notation.js" ,
279284 )
280- InputGroup , InputGroupText , FormControl , FormLabel = reactpy .web .export (
281- module , ["InputGroup" , "InputGroup.Text" , "Form.Control" , "Form.Label" ]
285+ InputGroup , InputGroupText , FormControl , FormLabel = (
286+ reactpy .web .module ._vdom_from_web_module (
287+ module , ["InputGroup" , "InputGroup.Text" , "Form.Control" , "Form.Label" ]
288+ )
282289 )
283290
284291 content = reactpy .html .div (
@@ -333,11 +340,13 @@ async def test_subcomponent_notation_as_str_attrs(display: DisplayFixture):
333340
334341
335342async def test_subcomponent_notation_as_obj_attrs (display : DisplayFixture ):
336- module = reactpy .web .module_from_file (
343+ module = reactpy .web .module . _module_from_file (
337344 "subcomponent-notation" ,
338345 JS_FIXTURES_DIR / "subcomponent-notation.js" ,
339346 )
340- InputGroup , Form = reactpy .web .export (module , ["InputGroup" , "Form" ])
347+ InputGroup , Form = reactpy .web .module ._vdom_from_web_module (
348+ module , ["InputGroup" , "Form" ]
349+ )
341350
342351 content = reactpy .html .div (
343352 {"id" : "the-parent" },
@@ -391,10 +400,10 @@ async def test_subcomponent_notation_as_obj_attrs(display: DisplayFixture):
391400
392401
393402async def test_callable_prop_with_javacript (display : DisplayFixture ):
394- module = reactpy .web .module_from_file (
403+ module = reactpy .web .module . _module_from_file (
395404 "callable-prop" , JS_FIXTURES_DIR / "callable-prop.js"
396405 )
397- Component = reactpy .web .export (module , "Component" )
406+ Component = reactpy .web .module . _vdom_from_web_module (module , "Component" )
398407
399408 @reactpy .component
400409 def App ():
@@ -411,7 +420,90 @@ def App():
411420 assert await my_div .inner_text () == "PREFIX TEXT: TEST 123"
412421
413422
414- def test_module_from_string ():
415- reactpy .web .module_from_string ("temp" , "old" )
423+ def test_import_js_from_string ():
424+ reactpy .web .import_js_from_string ("temp" , "old" , "Component" , resolve_exports = False )
425+ reactpy .web .module ._STRING_WEB_MODULE_CACHE .clear ()
416426 with assert_reactpy_did_log (r"Existing web module .* will be replaced with" ):
417- reactpy .web .module_from_string ("temp" , "new" )
427+ reactpy .web .import_js_from_string (
428+ "temp" , "new" , "Component" , resolve_exports = False
429+ )
430+
431+
432+ async def test_import_js_from_url (browser ):
433+ SimpleButton = reactpy .web .import_js_from_url (
434+ "/static/simple-button.js" , "SimpleButton" , resolve_exports = False
435+ )
436+
437+ @reactpy .component
438+ def ShowSimpleButton ():
439+ return SimpleButton ({"id" : "my-button" })
440+
441+ app = ReactPy (ShowSimpleButton )
442+ app = ServeStaticASGI (app , JS_FIXTURES_DIR , "/static/" )
443+
444+ async with BackendFixture (app ) as server :
445+ async with DisplayFixture (server , browser ) as display :
446+ await display .show (ShowSimpleButton )
447+
448+ await display .page .wait_for_selector ("#my-button" )
449+
450+
451+ async def test_import_js_from_file (display : DisplayFixture ):
452+ SimpleButton = reactpy .web .import_js_from_file (
453+ "simple-button" , JS_FIXTURES_DIR / "simple-button.js" , "SimpleButton"
454+ )
455+
456+ is_clicked = reactpy .Ref (False )
457+
458+ @reactpy .component
459+ def ShowSimpleButton ():
460+ return SimpleButton (
461+ {"id" : "my-button" , "onClick" : lambda event : is_clicked .set_current (True )}
462+ )
463+
464+ await display .show (ShowSimpleButton )
465+
466+ button = await display .page .wait_for_selector ("#my-button" )
467+ await button .click ()
468+ await poll (lambda : is_clicked .current ).until_is (True )
469+
470+
471+ def test_import_js_from_url_caching ():
472+ url = "https://example.com/module.js"
473+ reactpy .web .module ._URL_WEB_MODULE_CACHE .clear ()
474+
475+ # First import
476+ reactpy .web .import_js_from_url (url , "Component" , resolve_exports = False )
477+ assert url in reactpy .web .module ._URL_WEB_MODULE_CACHE
478+ module1 = reactpy .web .module ._URL_WEB_MODULE_CACHE [url ]
479+
480+ # Second import
481+ reactpy .web .import_js_from_url (url , "Component" , resolve_exports = False )
482+ assert reactpy .web .module ._URL_WEB_MODULE_CACHE [url ] is module1
483+
484+
485+ def test_import_js_from_file_caching (tmp_path ):
486+ file = tmp_path / "test.js"
487+ file .write_text ("export function Component() {}" )
488+ name = "test-file-module"
489+ reactpy .web .module ._FILE_WEB_MODULE_CACHE .clear ()
490+
491+ reactpy .web .import_js_from_file (name , file , "Component" )
492+ assert name in reactpy .web .module ._FILE_WEB_MODULE_CACHE
493+ module1 = reactpy .web .module ._FILE_WEB_MODULE_CACHE [name ]
494+
495+ reactpy .web .import_js_from_file (name , file , "Component" )
496+ assert reactpy .web .module ._FILE_WEB_MODULE_CACHE [name ] is module1
497+
498+
499+ def test_import_js_from_string_caching ():
500+ name = "test-string-module"
501+ content = "export function Component() {}"
502+ reactpy .web .module ._STRING_WEB_MODULE_CACHE .clear ()
503+
504+ reactpy .web .import_js_from_string (name , content , "Component" )
505+ assert name in reactpy .web .module ._STRING_WEB_MODULE_CACHE
506+ module1 = reactpy .web .module ._STRING_WEB_MODULE_CACHE [name ]
507+
508+ reactpy .web .import_js_from_string (name , content , "Component" )
509+ assert reactpy .web .module ._STRING_WEB_MODULE_CACHE [name ] is module1
0 commit comments