diff --git a/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/ElementStyleExtensions.kt b/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/ElementStyleExtensions.kt index 1ce096d..1b3b6e4 100644 --- a/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/ElementStyleExtensions.kt +++ b/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/ElementStyleExtensions.kt @@ -33,6 +33,7 @@ fun createElementStyle( legendText: String? = null, legendSprite: Sprite? = null, ): ElementStyle { + require(tag.isNotBlank()) { "tag cannot be blank" } val style = createElementStyleFromTag(tag) style.backgroundColor = backgroundColor style.fontColor = fontColor diff --git a/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/ViewSetStyleExtension.kt b/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/ViewSetStyleExtension.kt index fa6fd0e..6fe0976 100644 --- a/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/ViewSetStyleExtension.kt +++ b/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/ViewSetStyleExtension.kt @@ -9,3 +9,7 @@ fun ViewSet.addElementStyle(elementStyle: ElementStyle) { fun ViewSet.getElementStyles(): List = this.configuration.styles.elements.toList() + +internal fun ViewSet.clearElementStyles() { + this.configuration.styles.elements.clear() +} diff --git a/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/ViewStyleExtension.kt b/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/ViewStyleExtension.kt index eca73c5..37b685b 100644 --- a/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/ViewStyleExtension.kt +++ b/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/ViewStyleExtension.kt @@ -13,6 +13,5 @@ fun View.addElementStyle(elementStyle: ElementStyle) { fun View.getElementStyles(): List = this.properties - .filterKeys { it.startsWith(ELEMENT_STYLE_PROPERTY_NAME_PREFIX) } - .values - .map { elementStyleFromJson(it) } + .filter { it.key.startsWith(ELEMENT_STYLE_PROPERTY_NAME_PREFIX) } + .map { elementStyleFromJson(it.value) } diff --git a/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/sprite/ImageSprite.kt b/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/sprite/ImageSprite.kt index b2d6906..631a1d8 100644 --- a/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/sprite/ImageSprite.kt +++ b/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/sprite/ImageSprite.kt @@ -1,6 +1,25 @@ package com.github.chriskn.structurizrextension.api.view.style.sprite +import java.net.URI + +private val fileInUriRegex = "[^/\\\\&?]+\\.\\w{3,4}(?=([?&].*\$|\$))".toRegex() + data class ImageSprite( val url: String, - override val scale: Double? = null, -) : Sprite + val scale: Double? = null, +) : Sprite(scale) { + + init { + validateImageUrl(url) + } + + private fun validateImageUrl(urlString: String) { + val uri = URI(urlString) + val mathResult = fileInUriRegex.find(uri.schemeSpecificPart) + val fileWithEnding = mathResult?.groupValues?.first() + require(fileWithEnding?.contains(".") == true) { + "Image URI must point to a file" + } + require(uri.scheme == "img") { "Image URI must use img scheme" } + } +} diff --git a/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/sprite/OpenIconicSprite.kt b/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/sprite/OpenIconicSprite.kt index 8b79fd7..48bfa5a 100644 --- a/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/sprite/OpenIconicSprite.kt +++ b/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/sprite/OpenIconicSprite.kt @@ -1,7 +1,14 @@ package com.github.chriskn.structurizrextension.api.view.style.sprite +import com.fasterxml.jackson.annotation.JsonIgnore +import com.github.chriskn.structurizrextension.api.view.style.toValidColor + data class OpenIconicSprite( val name: String, val color: String? = null, - override val scale: Double? = null, -) : Sprite + val scale: Double? = null, +) : Sprite(scale) { + + @get:JsonIgnore + internal val validatedColor: String? = color?.let { toValidColor(color) } +} diff --git a/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/sprite/PumlSprite.kt b/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/sprite/PumlSprite.kt index c5a33af..121b703 100644 --- a/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/sprite/PumlSprite.kt +++ b/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/sprite/PumlSprite.kt @@ -1,9 +1,12 @@ package com.github.chriskn.structurizrextension.api.view.style.sprite +import com.fasterxml.jackson.annotation.JsonIgnore import com.github.chriskn.structurizrextension.api.icons.IconRegistry +import com.github.chriskn.structurizrextension.api.view.style.toValidColor import java.net.MalformedURLException data class PumlSprite( + val name: String, /** * Url used for include statement * @@ -11,12 +14,15 @@ data class PumlSprite( * @throws MalformedURLException if url is invalid */ val includeUrl: String, - val name: String, val color: String? = null, - override val scale: Double? = null, -) : Sprite { + val scale: Double? = null, +) : Sprite(scale) { + + @get:JsonIgnore + internal val validatedColor: String? = color?.let { toValidColor(color) } init { + require(name.isNotBlank()) { "name cannot be blank" } // TODO is the registry still needed? IconRegistry.addIcon(name, includeUrl) } diff --git a/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/sprite/Sprite.kt b/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/sprite/Sprite.kt index 9acb921..46debff 100644 --- a/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/sprite/Sprite.kt +++ b/src/main/kotlin/com/github/chriskn/structurizrextension/api/view/style/sprite/Sprite.kt @@ -16,7 +16,11 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo Type(value = PumlSprite::class, name = "PumlSprite"), ] ) -interface Sprite { +abstract class Sprite(scale: Double?) { - val scale: Double? + init { + require((scale ?: 1.0) > 0.0) { + "Scale must be greater than zero." + } + } } diff --git a/src/main/kotlin/com/github/chriskn/structurizrextension/internal/export/writer/ElementStyleWriter.kt b/src/main/kotlin/com/github/chriskn/structurizrextension/internal/export/writer/ElementStyleWriter.kt index 2ebd15b..5916e16 100644 --- a/src/main/kotlin/com/github/chriskn/structurizrextension/internal/export/writer/ElementStyleWriter.kt +++ b/src/main/kotlin/com/github/chriskn/structurizrextension/internal/export/writer/ElementStyleWriter.kt @@ -32,8 +32,11 @@ internal object ElementStyleWriter { .configuration .styles .elements - .filter { usedTags.contains(it.tag) } + view.getElementStyles() - return stylesForTags + .filter { usedTags.contains(it.tag) } + + view + .getElementStyles() + .filter { usedTags.contains(it.tag) } + return stylesForTags.distinctBy { it.tag } } fun writeElementStyle(elementStyle: ElementStyle, writer: IndentingWriter) { @@ -92,8 +95,8 @@ internal object ElementStyleWriter { } private fun Sprite.toPlantUmlString(): String = when (this) { - is PumlSprite -> """"${spriteString(this.name, scale, color)}"""" - is OpenIconicSprite -> """"&${spriteString(this.name, scale, color)}"""" + is PumlSprite -> """"${spriteString(this.name, scale, validatedColor)}"""" + is OpenIconicSprite -> """"&${spriteString(this.name, scale, validatedColor)}"""" is ImageSprite -> { val scaleString = scaleString(this.scale) if (scaleString.isBlank()) { diff --git a/src/test/kotlin/com/github/chriskn/structurizrextension/view/sprite/SpriteTest.kt b/src/test/kotlin/com/github/chriskn/structurizrextension/view/sprite/SpriteTest.kt index 07c16a5..8eef952 100644 --- a/src/test/kotlin/com/github/chriskn/structurizrextension/view/sprite/SpriteTest.kt +++ b/src/test/kotlin/com/github/chriskn/structurizrextension/view/sprite/SpriteTest.kt @@ -1,108 +1,119 @@ -// package com.github.chriskn.structurizrextension.view.sprite -// -// import com.github.chriskn.structurizrextension.api.view.style.createElementStyle -// import com.github.chriskn.structurizrextension.api.view.style.sprite -// import org.assertj.core.api.Assertions.assertThat -// import org.junit.jupiter.api.Test -// import org.junit.jupiter.api.assertThrows -// -// class SpriteTest { -// -// @Test -// fun `sprite is serialized and deserialized correctly when URI with scale is used`() { -// val expectedSprite = SpriteOld(URI.create("img:https://plantuml.com/logo.png"), 0.4) -// val style = createElementStyle("test", sprite = expectedSprite) -// -// assertThat(style.sprite).isEqualTo(expectedSprite) -// } -// -// @Test -// fun `sprite is serialized and deserialized correctly when URI without scale is used`() { -// val expectedSprite = SpriteOld(URI.create("img:https://plantuml.com/logo.png")) -// val style = createElementStyle("test", sprite = expectedSprite) -// -// assertThat(style.sprite).isEqualTo(expectedSprite) -// } -// -// @Test -// fun `sprite is serialized and deserialized correctly when openIconicIcon with scale is used`() { -// val expectedSprite = SpriteOld("folder", scale = 0.4) -// val style = createElementStyle("test", sprite = expectedSprite) -// -// assertThat(style.sprite).isEqualTo(expectedSprite) -// } -// -// @Test -// fun `sprite is serialized and deserialized correctly when openIconicIcon with color is used`() { -// val expectedSprite = SpriteOld("folder", color = "grey") -// val style = createElementStyle("test", sprite = expectedSprite) -// -// assertThat(style.sprite).isEqualTo(expectedSprite) -// } -// -// @Test -// fun `sprite is serialized and deserialized correctly when openIconicIcon with scale and color is used`() { -// val expectedSprite = SpriteOld("folder", color = "grey", scale = 0.1) -// val style = createElementStyle("test", sprite = expectedSprite) -// -// assertThat(style.sprite).isEqualTo(expectedSprite) -// } -// -// @Test -// fun `sprite is serialized and deserialized correctly when openIconicIcon without scale and color is used`() { -// val expectedSprite = SpriteOld("folder") -// val style = createElementStyle("test", sprite = expectedSprite) -// -// assertThat(style.sprite).isEqualTo(expectedSprite) -// } -// -// @Test -// fun `IllegalArgumentException is thrown when uri without image schema is used`() { -// assertThrows { -// SpriteOld(URI.create("https://plantuml.com/logo.png")) -// } -// } -// -// @Test -// fun `IllegalArgumentException is thrown when uri with different schema is used`() { -// assertThrows { -// SpriteOld(URI.create("file:https://plantuml.com/logo.png")) -// } -// } -// -// @Test -// fun `IllegalArgumentException is thrown when uri is not absolute`() { -// assertThrows { -// SpriteOld(URI.create("img:https://plantuml.com/")) -// } -// } -// -// @Test -// fun `IllegalArgumentException is thrown if scale is negative `() { -// assertThrows { -// SpriteOld(URI.create("img:https://plantuml.com//logo.png"), -0.1) -// } -// } -// -// @Test -// fun `IllegalArgumentException is thrown if scale is zero `() { -// assertThrows { -// SpriteOld(URI.create("img:https://plantuml.com//logo.png"), 0.0) -// } -// } -// -// @Test -// fun `sprite is serialized and deserialized correctly when openIconicIcon is used`() { -// val expectedSprite = SpriteOld("iconName", 0.4) -// val style = createElementStyle("test", sprite = expectedSprite) -// -// assertThat(style.sprite).isEqualTo(expectedSprite) -// } -// -// @Test -// fun `IllegalArgumentException is thrown for invalid sprite color color`() { -// assertThrows { -// SpriteOld("folder", color = "123") -// } -// } -// } +package com.github.chriskn.structurizrextension.view.sprite + +import com.github.chriskn.structurizrextension.api.icons.IconRegistry +import com.github.chriskn.structurizrextension.api.view.style.sprite.ImageSprite +import com.github.chriskn.structurizrextension.api.view.style.sprite.OpenIconicSprite +import com.github.chriskn.structurizrextension.api.view.style.sprite.PumlSprite +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows + +class SpriteTest { + + @Nested + inner class ImageSpriteTest { + + @Test + fun `IllegalArgumentException is thrown when uri does not point to file`() { + assertThrows { + ImageSprite("https://plantuml.com/logo") + } + } + + @Test + fun `IllegalArgumentException is thrown when uri without image schema is used`() { + assertThrows { + ImageSprite("https://plantuml.com/logo.png") + } + } + + @Test + fun `IllegalArgumentException is thrown when uri with different schema is used`() { + assertThrows { + ImageSprite("file:https://plantuml.com/logo.png") + } + } + + @Test + fun `IllegalArgumentException is thrown if scale is negative `() { + assertThrows { + ImageSprite("img:https://plantuml.com/logo.png", -0.1) + } + } + + @Test + fun `IllegalArgumentException is thrown if scale is zero `() { + assertThrows { + ImageSprite("img:https://plantuml.com/logo.png", 0.0) + } + } + } + + @Nested + inner class OpenIconicSpriteTest { + + @Test + fun `IllegalArgumentException is thrown for invalid OpenIconicSprite color`() { + assertThrows { + OpenIconicSprite("folder", color = "123") + } + } + } + + @Nested + inner class PumlSpriteTest { + + @Test + fun `IllegalArgumentException is thrown when name is blank`() { + val url = IconRegistry.iconUrlFor("kafka")!! + assertThrows { + PumlSprite(name = "", includeUrl = url) + } + } + + @Test + fun `IllegalArgumentException is thrown when url is blank`() { + assertThrows { + PumlSprite(name = " ", includeUrl = " ") + } + } + + @Test + fun `IllegalArgumentException is thrown for invalid PumlSprite color`() { + val name = IconRegistry.iconFileNameFor("kafka")!! + val url = IconRegistry.iconUrlFor("kafka")!! + + assertThrows { + PumlSprite(name = name, includeUrl = url, color = "123") + } + } + + @Test + fun `IllegalArgumentException is thrown when url does not point to a puml file`() { + assertThrows { + PumlSprite("test", "https://plantuml.com/logo.png") + } + } + + @Test + fun `IllegalArgumentException is thrown when url is invalid`() { + assertThrows { + PumlSprite("test", "plantuml.com/logo.png") + } + } + + @Test + fun `IllegalArgumentException is thrown if scale is negative `() { + assertThrows { + PumlSprite("test", "https://plantuml.com/logo.puml", scale = -0.1) + } + } + + @Test + fun `IllegalArgumentException is thrown if scale is zero `() { + assertThrows { + PumlSprite("test", "https://plantuml.com/logo.puml", scale = 0.0) + } + } + } +} diff --git a/src/test/kotlin/com/github/chriskn/structurizrextension/view/style/ElementStyleExtensionTest.kt b/src/test/kotlin/com/github/chriskn/structurizrextension/view/style/ElementStyleExtensionTest.kt index 273c05d..a87b5a1 100644 --- a/src/test/kotlin/com/github/chriskn/structurizrextension/view/style/ElementStyleExtensionTest.kt +++ b/src/test/kotlin/com/github/chriskn/structurizrextension/view/style/ElementStyleExtensionTest.kt @@ -1,20 +1,30 @@ package com.github.chriskn.structurizrextension.view.style +import com.github.chriskn.structurizrextension.api.icons.IconRegistry +import com.github.chriskn.structurizrextension.api.model.softwareSystem +import com.github.chriskn.structurizrextension.api.view.containerView import com.github.chriskn.structurizrextension.api.view.style.C4Shape.EIGHT_SIDED +import com.github.chriskn.structurizrextension.api.view.style.C4Shape.ROUNDED_BOX +import com.github.chriskn.structurizrextension.api.view.style.addElementStyle import com.github.chriskn.structurizrextension.api.view.style.backgroundColor import com.github.chriskn.structurizrextension.api.view.style.borderColor import com.github.chriskn.structurizrextension.api.view.style.borderWith import com.github.chriskn.structurizrextension.api.view.style.c4Shape import com.github.chriskn.structurizrextension.api.view.style.createElementStyle import com.github.chriskn.structurizrextension.api.view.style.fontColor +import com.github.chriskn.structurizrextension.api.view.style.getElementStyles import com.github.chriskn.structurizrextension.api.view.style.legendSprite import com.github.chriskn.structurizrextension.api.view.style.legendText import com.github.chriskn.structurizrextension.api.view.style.shadowing import com.github.chriskn.structurizrextension.api.view.style.sprite import com.github.chriskn.structurizrextension.api.view.style.sprite.ImageSprite import com.github.chriskn.structurizrextension.api.view.style.sprite.OpenIconicSprite +import com.github.chriskn.structurizrextension.api.view.style.sprite.PumlSprite import com.github.chriskn.structurizrextension.api.view.style.technology +import com.github.chriskn.structurizrextension.internal.export.view.style.toJson +import com.structurizr.Workspace import com.structurizr.view.Border.Dashed +import com.structurizr.view.Border.Dotted import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test @@ -66,6 +76,72 @@ class ElementStyleExtensionTest { assertThat(style.legendText).isEqualTo(expLegendText) } + @Test + fun `element style can be added to ViewSet`() { + val sprite = PumlSprite( + includeUrl = IconRegistry.iconUrlFor("postgresql")!!, + name = "postgresql", + scale = 0.5, + color = "green" + ) + val legendSprite = OpenIconicSprite("compass") + val style1 = createElementStyle( + tag = "tag", + backgroundColor = "#ffffff", + border = Dotted, + borderWith = 5, + borderColor = "purple", + fontColor = "red", + shadowing = false, + technology = "REST", + c4Shape = ROUNDED_BOX, + sprite = sprite, + legendSprite = legendSprite, + legendText = "this is a legend text" + ) + val style2 = createElementStyle("tag1") + + val workspace = Workspace("test", "test") + val views = workspace.views + + views.addElementStyle(style1) + views.addElementStyle(style2) + + assertThat(views.getElementStyles()).hasSize(2) + assertThat(views.getElementStyles()[0]).isEqualTo(style1) + assertThat(views.getElementStyles()[1]).isEqualTo(style2) + } + + @Test + fun `element style can be added to View`() { + val style1 = createElementStyle( + tag = "tag", + backgroundColor = "#ffffff", + border = Dotted, + borderWith = 5, + ) + val style2 = createElementStyle("tag1") + + val workspace = Workspace("test", "test") + val views = workspace.views + + val system = workspace.model.softwareSystem("test", "test") + val view = views.containerView(system, "testview", "desc") + view.addElementStyle(style1) + view.addElementStyle(style2) + + val elementStyles = view.getElementStyles() + assertThat(elementStyles).hasSize(2) + assertThat(elementStyles.map { it.toJson() }).contains(style1.toJson(), style2.toJson()) + } + + @Test + fun `IllegalArgumentException is thrown when tag is blank`() { + assertThrows { + createElementStyle(" ") + } + } + @Test fun `element style can be initialized with null values`() { val expTag = "tag" @@ -128,4 +204,104 @@ class ElementStyleExtensionTest { assertThat(elementStyle.backgroundColor).isEqualTo("#ffffff") } } + + @Nested + inner class Sprite { + + @Test + fun `PumlSprite is serialized and deserialized correctly`() { + val expectedSprite = PumlSprite( + name = "android", + includeUrl = "https://test.com/sprites/android-icon.puml", + ) + val style = createElementStyle("test", sprite = expectedSprite) + + assertThat(style.sprite).isEqualTo(expectedSprite) + } + + @Test + fun `PumlSprite is serialized and deserialized correctly when color is used`() { + val expectedSprite = PumlSprite( + name = "android", + includeUrl = "https://test.com/sprites/android-icon.puml", + color = "green" + ) + val style = createElementStyle("test", sprite = expectedSprite) + + assertThat(style.sprite).isEqualTo(expectedSprite) + } + + @Test + fun `PumlSprite is serialized and deserialized correctly when scale is used`() { + val expectedSprite = PumlSprite( + name = "android", + includeUrl = "https://test.com/sprites/android-icon.puml", + scale = 0.4 + ) + val style = createElementStyle("test", sprite = expectedSprite) + + assertThat(style.sprite).isEqualTo(expectedSprite) + } + + @Test + fun `PumlSprite is serialized and deserialized correctly when scale and color is used`() { + val expectedSprite = PumlSprite( + name = "android", + includeUrl = "https://test.com/sprites/android-icon.puml", + scale = 0.4, + color = "green" + ) + val style = createElementStyle("test", sprite = expectedSprite) + + assertThat(style.sprite).isEqualTo(expectedSprite) + } + + @Test + fun `OpenIconicSprite is serialized and deserialized correctly`() { + val expectedSprite = OpenIconicSprite("folder") + val style = createElementStyle("test", sprite = expectedSprite) + + assertThat(style.sprite).isEqualTo(expectedSprite) + } + + @Test + fun `OpenIconicSprite is serialized and deserialized correctly with scale`() { + val expectedSprite = OpenIconicSprite("folder", scale = 0.4) + val style = createElementStyle("test", sprite = expectedSprite) + + assertThat(style.sprite).isEqualTo(expectedSprite) + } + + @Test + fun `OpenIconicSprite is serialized and deserialized correctly with color`() { + val expectedSprite = OpenIconicSprite("folder", color = "grey") + val style = createElementStyle("test", sprite = expectedSprite) + + assertThat(style.sprite).isEqualTo(expectedSprite) + } + + @Test + fun `OpenIconicSprite is serialized and deserialized correctly with scale and color`() { + val expectedSprite = OpenIconicSprite("folder", color = "grey", scale = 0.1) + val style = createElementStyle("test", sprite = expectedSprite) + + assertThat(style.sprite).isEqualTo(expectedSprite) + } + + @Test + fun `ImageSprite is serialized and deserialized correctly when URI with scale is used`() { + val expectedSprite = ImageSprite("img:https://plantuml.com/logo.png", 0.4) + val style = createElementStyle("test", sprite = expectedSprite) + + assertThat(style.sprite).isEqualTo(expectedSprite) + } + + @Test + fun `ImageSprite is serialized and deserialized correctly when URI without scale is used`() { + val expectedSprite = ImageSprite("img:https://plantuml.com/logo.png") + val style = createElementStyle("test", sprite = expectedSprite) + + assertThat(style.sprite).isEqualTo(expectedSprite) + } + } } diff --git a/src/test/kotlin/com/github/chriskn/structurizrextension/view/style/ElementStyleIntegrationTest.kt b/src/test/kotlin/com/github/chriskn/structurizrextension/view/style/ElementStyleIntegrationTest.kt index f296281..59c5665 100644 --- a/src/test/kotlin/com/github/chriskn/structurizrextension/view/style/ElementStyleIntegrationTest.kt +++ b/src/test/kotlin/com/github/chriskn/structurizrextension/view/style/ElementStyleIntegrationTest.kt @@ -8,6 +8,7 @@ import com.github.chriskn.structurizrextension.api.view.containerView import com.github.chriskn.structurizrextension.api.view.style.C4Shape.EIGHT_SIDED import com.github.chriskn.structurizrextension.api.view.style.C4Shape.ROUNDED_BOX import com.github.chriskn.structurizrextension.api.view.style.addElementStyle +import com.github.chriskn.structurizrextension.api.view.style.clearElementStyles import com.github.chriskn.structurizrextension.api.view.style.createElementStyle import com.github.chriskn.structurizrextension.api.view.style.sprite.ImageSprite import com.github.chriskn.structurizrextension.api.view.style.sprite.OpenIconicSprite @@ -17,6 +18,7 @@ import com.github.chriskn.structurizrextension.assertExpectedDiagramWasWrittenFo import com.structurizr.Workspace import com.structurizr.view.Border.Dashed import com.structurizr.view.Border.Dotted +import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test class ElementStyleIntegrationTest { @@ -35,6 +37,11 @@ class ElementStyleIntegrationTest { private val container = system.container("My Container", "container", tags = listOf(containerTag)) private val component = container.component("My Component", "component", tags = listOf(componentTag)) + @BeforeEach + fun resetStyles() { + views.clearElementStyles() + } + @Test fun `element style is applied for software systems`() { val diagramKey = "SystemStyleTest" @@ -97,7 +104,7 @@ class ElementStyleIntegrationTest { @Test fun `element style can be applied for single views`() { - val diagramKeyWithStyle = "ContainerStyleTest" + val diagramKeyWithStyle = "ViewStyleTestWithStyle" val diagramKeyWithoutStyle = "ViewStyleTestWithoutStyle" val sprite = PumlSprite( @@ -107,7 +114,7 @@ class ElementStyleIntegrationTest { color = "green" ) val legendSprite = OpenIconicSprite("compass") - val containerStyle = createElementStyle( + val containerTag = createElementStyle( tag = containerTag, backgroundColor = "#ffffff", border = Dotted, @@ -122,9 +129,9 @@ class ElementStyleIntegrationTest { legendText = "this is a legend text" ) val containerViewWithStyle = - workspace.views.containerView(system, diagramKeyWithStyle, "ContainerStyleTest") + workspace.views.containerView(system, diagramKeyWithStyle, "ViewStyleTestWithStyle") containerViewWithStyle.addAllContainers() - containerViewWithStyle.addElementStyle(containerStyle) + containerViewWithStyle.addElementStyle(containerTag) val containerViewWithoutStyle = workspace.views.containerView(system, diagramKeyWithoutStyle, "ViewStyleTestWithoutStyle") containerViewWithoutStyle.addAllContainers() @@ -132,4 +139,21 @@ class ElementStyleIntegrationTest { assertExpectedDiagramWasWrittenForView(workspace, pathToExpectedDiagrams, diagramKeyWithStyle) assertExpectedDiagramWasWrittenForView(workspace, pathToExpectedDiagrams, diagramKeyWithoutStyle) } + + @Test + fun `element style for unused tags are not exported`() { + val diagramKey = "ViewStyleTestUnusedTag" + + val unusedStyle = createElementStyle( + tag = "someUnusedTag", + backgroundColor = "#ffffff", + legendText = "this is a legend text" + ) + val containerViewWithStyle = + workspace.views.containerView(system, diagramKey, "ViewStyleTestWithStyle") + containerViewWithStyle.addAllContainers() + containerViewWithStyle.addElementStyle(unusedStyle) + + assertExpectedDiagramWasWrittenForView(workspace, pathToExpectedDiagrams, diagramKey) + } } diff --git a/src/test/kotlin/com/github/chriskn/structurizrextension/view/style/ElementStyleJsonTest.kt b/src/test/kotlin/com/github/chriskn/structurizrextension/view/style/ElementStyleJsonTest.kt index e055521..648e730 100644 --- a/src/test/kotlin/com/github/chriskn/structurizrextension/view/style/ElementStyleJsonTest.kt +++ b/src/test/kotlin/com/github/chriskn/structurizrextension/view/style/ElementStyleJsonTest.kt @@ -23,7 +23,7 @@ import org.junit.jupiter.api.Test class ElementStyleJsonTest { @Test - fun `ElementStyle is persisted correctly`() { + fun `ElementStyle is serialized and deserialized correctly`() { val expSprite = ImageSprite("img:https://plantuml.com/logo3.png", 0.4) val expLegendSprite = OpenIconicSprite("compass", scale = 3.0, color = "blue") val expTag = "styleTag" diff --git a/src/test/kotlin/com/github/chriskn/structurizrextension/view/style/test.json b/src/test/kotlin/com/github/chriskn/structurizrextension/view/style/test.json deleted file mode 100644 index d734e5e..0000000 --- a/src/test/kotlin/com/github/chriskn/structurizrextension/view/style/test.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "properties": { - "c4:sprite": "{\"@type\":\"ImageSprite\",\"url\":\"img:https://plantuml.com/logo3.png\",\"scale\":0.4}", - "c4:borderColor": "#008000", - "c4:legendSprite": "{\"@type\":\"OpenIconicSprite\",\"name\":\"compass\",\"color\":\"blue\",\"scale\":3.0}", - "c4:legendText": "this is a legend", - "c4:technology": "Kafka", - "c4:shadowing": "true", - "c4:shape": "EIGHT_SIDED" - }, - "tag": "styleTag", - "background": "#000000", - "strokeWidth": 4, - "color": "#ffffff", - "border": "Dashed" -} \ No newline at end of file diff --git a/src/test/resources/expected/view/style/ContainerStyleTest.puml b/src/test/resources/expected/view/style/ContainerStyleTest.puml index a2f45bf..8e39df3 100644 --- a/src/test/resources/expected/view/style/ContainerStyleTest.puml +++ b/src/test/resources/expected/view/style/ContainerStyleTest.puml @@ -7,7 +7,7 @@ caption ContainerStyleTest SHOW_PERSON_OUTLINE() LAYOUT_TOP_DOWN() -AddElementTag(Container Style, $sprite="postgresql{scale=0.5,color=green}", $bgColor=#ffffff, $fontColor=#ff0000, $borderColor=#800080, $borderStyle=DottedLine(), $borderThickness=5, $shadowing=false, $shape=RoundedBoxShape(), $techn=REST, $legendSprite="&compass", $legendText=this is a legend text) +AddElementTag(Container Style, $sprite="postgresql{scale=0.5,color=#008000}", $bgColor=#ffffff, $fontColor=#ff0000, $borderColor=#800080, $borderStyle=DottedLine(), $borderThickness=5, $shadowing=false, $shape=RoundedBoxShape(), $techn=REST, $legendSprite="&compass", $legendText=this is a legend text) Container(MySoftwareSystem.MyContainer, "My Container", "", "container", "", $tags="Container Style" ) diff --git a/src/test/resources/expected/view/style/ViewStyleTestUnusedTag.puml b/src/test/resources/expected/view/style/ViewStyleTestUnusedTag.puml new file mode 100644 index 0000000..69bf68d --- /dev/null +++ b/src/test/resources/expected/view/style/ViewStyleTestUnusedTag.puml @@ -0,0 +1,14 @@ +@startuml(id=ViewStyleTestUnusedTag) +!includeurl https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml +title My Software System - Containers +caption ViewStyleTestWithStyle + +SHOW_PERSON_OUTLINE() +LAYOUT_TOP_DOWN() + +Container(MySoftwareSystem.MyContainer, "My Container", "", "container", "", $tags="Container Style" ) + + +SHOW_LEGEND(true) + +@enduml \ No newline at end of file diff --git a/src/test/resources/expected/view/style/ViewStyleTestWithStyle.puml b/src/test/resources/expected/view/style/ViewStyleTestWithStyle.puml new file mode 100644 index 0000000..58514f5 --- /dev/null +++ b/src/test/resources/expected/view/style/ViewStyleTestWithStyle.puml @@ -0,0 +1,16 @@ +@startuml(id=ViewStyleTestWithStyle) +!includeurl https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml +!includeurl https://raw.githubusercontent.com/plantuml-stdlib/gilbarbara-plantuml-sprites/master/sprites/postgresql.puml +title My Software System - Containers +caption ViewStyleTestWithStyle + +SHOW_PERSON_OUTLINE() +LAYOUT_TOP_DOWN() + +AddElementTag(Container Style, $sprite="postgresql{scale=0.5,color=#008000}", $bgColor=#ffffff, $fontColor=#ff0000, $borderColor=#800080, $borderStyle=DottedLine(), $borderThickness=5, $shadowing=false, $shape=RoundedBoxShape(), $techn=REST, $legendSprite="&compass", $legendText=this is a legend text) +Container(MySoftwareSystem.MyContainer, "My Container", "", "container", "", $tags="Container Style" ) + + +SHOW_LEGEND(true) + +@enduml \ No newline at end of file