@@ -164,6 +164,74 @@ def test_browser_report_does_not_delete_or_overwrite_preexisting_assets(tmp_path
164164 assert existing_css .read_text (encoding = "utf-8" ) == "/* custom preexisting css */"
165165
166166
167+ import copy
168+
169+
170+ def test_browser_report_title_usable (tmp_path ):
171+ report_spec = {"title" : "Summary Report" , "sections" : []}
172+ output = RtichokeBrowserReport (report_spec ).write_html (tmp_path / "report.html" )
173+ html_content = output .read_text (encoding = "utf-8" )
174+ assert "<title>Summary Report</title>" in html_content
175+
176+
177+ def test_browser_report_title_preserves_unstripped_usable_string (tmp_path ):
178+ report_spec = {"title" : " Summary Report " , "sections" : []}
179+ output = RtichokeBrowserReport (report_spec ).write_html (tmp_path / "report.html" )
180+ html_content = output .read_text (encoding = "utf-8" )
181+ assert "<title> Summary Report </title>" in html_content
182+
183+
184+ @pytest .mark .parametrize (
185+ "invalid_title" ,
186+ [
187+ None ,
188+ "" ,
189+ " " ,
190+ "\t \n " ,
191+ 123 ,
192+ ["Summary Report" ],
193+ {"a" : "b" },
194+ ],
195+ )
196+ def test_browser_report_title_fallback (tmp_path , invalid_title ):
197+ spec = {} if invalid_title is None else {"title" : invalid_title }
198+ output = RtichokeBrowserReport (spec ).write_html (tmp_path / "report.html" )
199+ html_content = output .read_text (encoding = "utf-8" )
200+ assert "<title>rtichoke report</title>" in html_content
201+
202+
203+ def test_browser_report_title_html_escaped (tmp_path ):
204+ title_with_special = "Summary & Performance <Report> 'v1.0' \" Draft\" "
205+ report_spec = {"title" : title_with_special , "sections" : []}
206+ output = RtichokeBrowserReport (report_spec ).write_html (tmp_path / "report.html" )
207+ html_content = output .read_text (encoding = "utf-8" )
208+ expected_title = "Summary & Performance <Report> 'v1.0' "Draft""
209+ assert f"<title>{ expected_title } </title>" in html_content
210+
211+
212+ def test_browser_report_does_not_mutate_spec (tmp_path ):
213+ original_spec = {
214+ "title" : "Summary Report" ,
215+ "sections" : [],
216+ "value_nan" : float ("nan" ),
217+ }
218+ spec_copy = copy .deepcopy (original_spec )
219+
220+ report = RtichokeBrowserReport (original_spec )
221+ output = report .write_html (tmp_path / "report.html" )
222+ html_content = output .read_text (encoding = "utf-8" )
223+
224+ assert report .spec is original_spec
225+ # original_spec itself should not be mutated
226+ assert repr (original_spec ["value_nan" ]) == repr (spec_copy ["value_nan" ])
227+ assert original_spec ["title" ] == "Summary Report"
228+
229+ # Embedded spec in HTML is sanitized (NaN -> None)
230+ embedded = _embedded_report (html_content )
231+ assert embedded ["title" ] == "Summary Report"
232+ assert embedded ["value_nan" ] is None
233+
234+
167235def test_resolve_render_report_symbol_export_clauses ():
168236 # Unminified / shorthand export
169237 shorthand_js = "export { foo, renderReport, bar };"
0 commit comments