@@ -412,5 +412,155 @@ object DatastarEventSpec extends ZIOSpecDefault {
412412 )
413413 },
414414 ),
415+ suite(" PatchElements with multi-line content" )(
416+ test(" handles script tags with multi-line JavaScript" ) {
417+ val scriptContent = """ console.log('line 1');
418+ |console.log('line 2');
419+ |console.log('line 3');""" .stripMargin
420+ val event = DatastarEvent .patchElements(
421+ Dom .script(scriptContent),
422+ )
423+
424+ val sse = event.toServerSentEvent
425+
426+ assertTrue(
427+ sse.data == """ elements <script>console.log('line 1');
428+ |elements console.log('line 2');
429+ |elements console.log('line 3');</script>
430+ |""" .stripMargin,
431+ )
432+ },
433+ test(" handles style tags with multi-line CSS" ) {
434+ val cssContent = """ .button {
435+ | color: red;
436+ | background: blue;
437+ |}""" .stripMargin
438+ val event = DatastarEvent .patchElements(
439+ Dom .style(cssContent),
440+ )
441+
442+ val sse = event.toServerSentEvent
443+
444+ assertTrue(
445+ sse.data == """ elements <style>.button {
446+ |elements color: red;
447+ |elements background: blue;
448+ |elements }</style>
449+ |""" .stripMargin,
450+ )
451+ },
452+ test(" handles inline style attribute with newlines" ) {
453+ val styleContent = """ color: red;
454+ |background: blue;
455+ |padding: 10px;""" .stripMargin
456+ val event = DatastarEvent .patchElements(
457+ div(Dom .attr(" style" , styleContent))(" Content" ),
458+ )
459+
460+ val sse = event.toServerSentEvent
461+
462+ // The minified output should handle newlines in attribute values
463+ assertTrue(
464+ sse.data.startsWith(" elements " ) &&
465+ sse.data.contains(" style=" ) &&
466+ sse.data.contains(" Content" ),
467+ )
468+ },
469+ test(" handles complex HTML with embedded script and style" ) {
470+ val event = DatastarEvent .patchElements(
471+ div(
472+ Dom .style(""" .test {
473+ | color: red;
474+ |}""" .stripMargin),
475+ Dom .script(""" console.log('test');
476+ |alert('hello');""" .stripMargin),
477+ p(" Content" ),
478+ ),
479+ )
480+
481+ val sse = event.toServerSentEvent
482+
483+ // Should split on newlines and prefix each line with "elements "
484+ val lines = sse.data.split('\n ' ).filter(_.nonEmpty)
485+ assertTrue(
486+ lines.forall(_.startsWith(" elements " )),
487+ lines.length > 1 , // Multi-line content
488+ sse.data.contains(" .test" ),
489+ sse.data.contains(" console.log" ),
490+ sse.data.contains(" Content" ),
491+ )
492+ },
493+ test(" handles single-line content without extra splitting" ) {
494+ val event = DatastarEvent .patchElements(
495+ div(" Simple content" ),
496+ )
497+
498+ val sse = event.toServerSentEvent
499+
500+ assertTrue(
501+ sse.data == " elements <div>Simple content</div>\n " ,
502+ )
503+ },
504+ test(" handles script with selector and mode options" ) {
505+ val scriptContent = """ function init() {
506+ | console.log('initialized');
507+ |}""" .stripMargin
508+ val event = DatastarEvent .patchElements(
509+ Dom .script(scriptContent),
510+ Some (selector " #app " ),
511+ ElementPatchMode .Append ,
512+ )
513+
514+ val sse = event.toServerSentEvent
515+
516+ assertTrue(
517+ sse.data.contains(" selector #app\n " ),
518+ sse.data.contains(" mode append\n " ),
519+ sse.data.contains(" elements <script>function init() {\n " ),
520+ sse.data.contains(" elements console.log('initialized');\n " ),
521+ sse.data.contains(" elements }</script>\n " ),
522+ )
523+ },
524+ test(" handles CSS with minification and newlines" ) {
525+ val cssContent = """ .container {
526+ | display: flex;
527+ | justify-content: center;
528+ |}
529+ |.item {
530+ | margin: 5px;
531+ |}""" .stripMargin
532+ val event = DatastarEvent .patchElements(
533+ Dom .style(cssContent),
534+ )
535+
536+ val sse = event.toServerSentEvent
537+
538+ // Each line should be prefixed with "elements "
539+ val lines = sse.data.split('\n ' ).filter(_.nonEmpty)
540+ assertTrue(
541+ lines.forall(_.startsWith(" elements " )),
542+ lines.length >= 5 , // Multiple lines from CSS
543+ sse.data.contains(" container" ),
544+ sse.data.contains(" display" ),
545+ sse.data.contains(" item" ),
546+ )
547+ },
548+ test(" ExecuteScript should also handle multi-line correctly" ) {
549+ val scriptContent = """ const x = 1;
550+ |const y = 2;
551+ |console.log(x + y);""" .stripMargin
552+ val event = DatastarEvent .executeScript(scriptContent)
553+
554+ val sse = event.toServerSentEvent
555+
556+ assertTrue(
557+ sse.data.contains(" selector <body></body>\n " ),
558+ sse.data.contains(" mode append\n " ),
559+ sse.data.contains(" elements <script data-effect=\" el.remove\" >const x = 1;\n " ),
560+ sse.data.contains(" elements const y = 2;\n " ),
561+ sse.data.contains(" elements console.log(x + y);</script>\n " ),
562+ )
563+ },
564+ ),
415565 )
416566}
0 commit comments