diff --git a/scala3doc/src/dotty/renderers/html.scala b/scala3doc/src/dotty/renderers/html.scala index a97f325fa348..28d0572ea96e 100644 --- a/scala3doc/src/dotty/renderers/html.scala +++ b/scala3doc/src/dotty/renderers/html.scala @@ -14,7 +14,7 @@ object HTML: def apply(attrs: AttrArg*)(tags: TagArg*): AppliedTag = { val sb = StringBuilder() sb.append(s"<$name") - attrs.foreach{ + attrs.filter(_ != Nil).foreach{ case s: Seq[AppliedAttr] => s.foreach(sb.append(" ").append) case e: AppliedAttr => @@ -25,19 +25,26 @@ object HTML: case t: AppliedTag => sb.append(t) case s: String => - sb.append(s) + sb.append(s.escapeReservedTokens) case s: Seq[AppliedTag | String] => s.foreach{ case a: AppliedTag => sb.append(a) case s: String => - sb.append(s) + sb.append(s.escapeReservedTokens) } } sb.append(s"") sb } + extension (s: String) private def escapeReservedTokens: String = + s.replace("&", "&") + .replace("<", "<") + .replace(">", ">") + .replace("\"", """) + .replace("'", "'") + case class Attr(name: String): def :=(value: String): AppliedAttr = AppliedAttr(s"""$name="$value"""") @@ -48,6 +55,7 @@ object HTML: val div = Tag("div") val span = Tag("span") val a = Tag("a") + val p = Tag("p") val h1 = Tag("h1") val h2 = Tag("h2") val h3 = Tag("h3") diff --git a/scala3doc/test/dotty/dokka/renderers/HtmlTagsTest.scala b/scala3doc/test/dotty/dokka/renderers/HtmlTagsTest.scala new file mode 100644 index 000000000000..fca9c3067041 --- /dev/null +++ b/scala3doc/test/dotty/dokka/renderers/HtmlTagsTest.scala @@ -0,0 +1,197 @@ +package dotty.dokka.renderers + +import org.junit.{Test, Rule} +import org.junit.Assert.{assertSame, assertTrue, assertEquals} +import dotty.dokka.HTML._ + +class HtmlTagsTest { + + @Test + def simpleDiv = { + val actual = div().toString + val expect = "
" + assertEquals(expect, actual) + } + + @Test + def divWithStyles = { + val actual = div(style := "some: style;")().toString + val expect = """
""" + assertEquals(expect, actual) + } + + @Test + def divWithChildren = { + val actual = div(h1(), span()).toString + val expect = """

""" + assertEquals(expect, actual) + } + + @Test + def divWithTextInside = { + val actual = div(h1(), span("Some text"), "Some more of the text").toString + val expect = """

Some textSome more of the text
""" + assertEquals(expect, actual) + } + + @Test + def escapeAmpersand = { + val actual = div("Some & text").toString + val expect = """
Some & text
""" + assertEquals(expect, actual) + } + + @Test + def escapeLessThan = { + val actual = div("Some < text").toString + val expect = """
Some < text
""" + assertEquals(expect, actual) + } + + @Test + def escapeGreaterThan = { + val actual = div("Some > text").toString + val expect = """
Some > text
""" + assertEquals(expect, actual) + } + + @Test + def escapeQuotationMark = { + val actual = div("Some \" text").toString + val expect = """
Some " text
""" + assertEquals(expect, actual) + } + + @Test + def escapeApostrophe = { + val actual = div("Some ' text").toString + val expect = """
Some ' text
""" + assertEquals(expect, actual) + } + + @Test + def nestedTagsWithAttributes = { + val actual = html( + head( + script(src:="..."), + script(raw("alert('Hello World')")) + ), + body( + div( + h1(id:="title")("This is a title"), + p("This is a big paragraph of text") + ) + ) + ).toString + val expect = """

This is a title

This is a big paragraph of text

""" + assertEquals(expect, actual) + } + + @Test + def anotherNestedTagsWithAttributes = { + val actual = html( + head( + script("some script") + ), + body( + h1(style:="background-color: blue; color: red;")("This is my title"), + div(style:="background-color: blue; color: red;")( + p(cls :="contentpara first")( + "This is my first paragraph" + ), + a(style:="opacity: 0.9;")( + p(cls := "contentpara")("Goooogle") + ) + ) + ) + ).toString + val expect = """

This is my title

This is my first paragraph

Goooogle

""" + assertEquals(expect, actual) + } + + @Test + def appliedTagAndSeqAppliedTag = { + val actual = div(h1("AppliedTag"), Seq(h1("SeqAppliedTag"))).toString + val expect = """

AppliedTag

SeqAppliedTag

""" + assertEquals(expect, actual) + } + + @Test + def stringAndSeqString = { + val actual = div("String", Seq("SeqString")).toString + val expect = """
StringSeqString
""" + assertEquals(expect, actual) + } + + @Test + def mixingAllTagArgs = { + val actual = div("String", Seq("SeqString"), h1("AppliedTag"), Seq(h1("SeqAppliedTag")), Seq("SeqString"), h1("AppliedTag")).toString + val expect = """
StringSeqString

AppliedTag

SeqAppliedTag

SeqString

AppliedTag

""" + assertEquals(expect, actual) + } + + @Test + def appliedAttrAndSeqAppliedAttr = { + val actual = div(cls := "someClass", Seq(style := "some: style;")).toString + val expect = """
""" + assertEquals(expect, actual) + } + + @Test + def seqAppliedAttrAndAppliedAttr = { + val actual = div(Seq(cls := "someClass"), style := "some: style;").toString + val expect = """
""" + assertEquals(expect, actual) + } + + @Test + def seqAppliedAttrAndSeqAppliedAttr = { + val actual = div(Seq(cls := "someClass"), Seq(style := "some: style;")).toString + val expect = """
""" + assertEquals(expect, actual) + } + + @Test + def mixingAllTagArgsAndAllAttrArgs = { + val actual = div(Seq(cls := "someClass"), id := "myId", Seq(style := "some: style;"))("String", Seq("SeqString"), h1("AppliedTag"), Seq(h1("SeqAppliedTag")), Seq("SeqString"), h1("AppliedTag")).toString + val expect = """
StringSeqString

AppliedTag

SeqAppliedTag

SeqString

AppliedTag

""" + assertEquals(expect, actual) + } + + @Test + def nilTagArg = { + val nil: TagArg = Nil + val actual = div(nil).toString + val expect = """
""" + assertEquals(expect, actual) + } + + @Test + def nilAttrArg = { + val nil: AttrArg = Nil + val actual = div(nil).toString + val expect = """
""" + assertEquals(expect, actual) + } + + @Test + def nilAmongTags = { + val actual = div("name", Nil, div("ala")).toString + val expect = """
name
ala
""" + assertEquals(expect, actual) + } + + @Test + def nilAmongArgs = { + val actual = div(cls := "someClass", Nil, style := "some: style;").toString + val expect = """
""" + assertEquals(expect, actual) + } + + @Test + def nilAmongArgsAndTags = { + val actual = div(cls := "someClass", Nil, style := "some: style;")("name", Nil, div("ala")).toString + val expect = """
name
ala
""" + assertEquals(expect, actual) + } +} \ No newline at end of file