You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by jo...@apache.org on 2021/04/14 15:28:13 UTC

[isis] 02/04: [ISIS-2505] preparation for sync request tests, merge tab layout

This is an automated email from the ASF dual-hosted git repository.

joergrade pushed a commit to branch ISIS-2505_Catch_Up_With_Demo_Examples
in repository https://gitbox.apache.org/repos/asf/isis.git

commit b4e4d3a82f2a3d9701594949a3800faa2413b5a8
Author: Jörg Rade <jo...@kuehne-nagel.com>
AuthorDate: Tue Apr 6 11:28:27 2021 +0200

    [ISIS-2505] preparation for sync request tests, merge tab layout
---
 incubator/clients/kroviz/build.gradle.kts          |   2 +-
 incubator/clients/kroviz/karma.config.d/proxy.js   |  11 ++
 .../kotlin/org/apache/isis/client/kroviz/App.kt    |   2 +-
 .../kroviz/core/aggregator/ActionDispatcher.kt     |  34 ++--
 .../core/aggregator/DomainTypesAggregator.kt       |   3 +-
 .../kroviz/core/aggregator/ListAggregator.kt       |   2 +-
 .../kroviz/core/aggregator/ObjectAggregator.kt     |  11 +-
 .../isis/client/kroviz/core/model/ObjectDM.kt      |  20 +++
 .../org/apache/isis/client/kroviz/to/bs3/Col.kt    |  10 +-
 .../apache/isis/client/kroviz/ui/EventLogDetail.kt |  13 +-
 .../client/kroviz/ui/builder/FieldSetBuilder.kt    |   1 -
 .../isis/client/kroviz/ui/kv/EventLogTable.kt      |   8 +-
 .../apache/isis/client/kroviz/ui/kv/RoDisplay.kt   |   3 +
 .../apache/isis/client/kroviz/ui/kv/RoIconBar.kt   |   6 +-
 .../apache/isis/client/kroviz/ui/kv/RoWindow.kt    |  43 +----
 .../org/apache/isis/client/kroviz/utils/DomUtil.kt |   2 +-
 .../apache/isis/client/kroviz/utils/IconManager.kt |   1 +
 .../apache/isis/client/kroviz/utils/XmlHelper.kt   |  25 +++
 .../kroviz/src/main/resources/img/claever.svg      |  40 +++--
 .../kroviz/snapshots/ResponseRegressionTest.kt     |  26 ++-
 .../isis/client/kroviz/snapshots/SyncRequest.kt    |  27 ---
 .../isis/client/kroviz/snapshots/TestRequest.kt    |  36 ++++
 .../demo2_0_0/ACTIONS_WHEREINTHEWORLD_INVOKE.kt    | 198 +++++++++++++++++++++
 .../kroviz/snapshots/demo2_0_0/Response2Handler.kt |   3 +-
 .../isis/client/kroviz/snapshots/sample.json       | 119 +++++++++----
 incubator/clients/kroviz/src/test/setup.js         |   1 +
 26 files changed, 478 insertions(+), 169 deletions(-)

diff --git a/incubator/clients/kroviz/build.gradle.kts b/incubator/clients/kroviz/build.gradle.kts
index a7af57b..fe0c2cb 100644
--- a/incubator/clients/kroviz/build.gradle.kts
+++ b/incubator/clients/kroviz/build.gradle.kts
@@ -117,8 +117,8 @@ kotlin {
     }
     sourceSets["test"].dependencies {
         implementation(kotlin("test-js"))
-        implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core-js:1.3.3")
         implementation("pl.treksoft:kvision-testutils:$kvisionVersion:tests")
+        implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core-js:1.4.2")
     }
     sourceSets["main"].resources.srcDir(webDir)
 }
diff --git a/incubator/clients/kroviz/karma.config.d/proxy.js b/incubator/clients/kroviz/karma.config.d/proxy.js
index 4fe5613..1ab1ca5 100644
--- a/incubator/clients/kroviz/karma.config.d/proxy.js
+++ b/incubator/clients/kroviz/karma.config.d/proxy.js
@@ -19,3 +19,14 @@
 
 
 config.singleRun = true;
+
+// @link https://stackoverflow.com/questions/61839800/unit-testing-in-kotlin-js
+const path = require('path');
+const resourcesSourcePath = path.resolve(__dirname, '../../../../build/processedResources/js/main');
+const setupFile = path.resolve(__dirname, '../../../../src/test/setup.js');
+config.files.unshift(setupFile);
+config.proxies = {
+    "/strings/": "absolute" + resourcesSourcePath + "/strings/",
+    "/css/": "absolute" + resourcesSourcePath + "/css/",
+    "/images/": "absolute" + resourcesSourcePath + "/images/"
+}
diff --git a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/App.kt b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/App.kt
index c2acf28..c9722be 100644
--- a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/App.kt
+++ b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/App.kt
@@ -31,7 +31,7 @@ class App : Application() {
 
     init {
         require("css/kroviz.css")
-        require("lodash")
+//        require("lodash")
     }
 
     override fun start() {
diff --git a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/core/aggregator/ActionDispatcher.kt b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/core/aggregator/ActionDispatcher.kt
index 5ee6df8..8dd7121 100644
--- a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/core/aggregator/ActionDispatcher.kt
+++ b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/core/aggregator/ActionDispatcher.kt
@@ -19,26 +19,38 @@
 package org.apache.isis.client.kroviz.core.aggregator
 
 import org.apache.isis.client.kroviz.core.event.LogEntry
-import org.apache.isis.client.kroviz.to.Action
-import org.apache.isis.client.kroviz.to.Link
-import org.apache.isis.client.kroviz.to.Method
-import org.apache.isis.client.kroviz.to.Relation
+import org.apache.isis.client.kroviz.to.*
 import org.apache.isis.client.kroviz.ui.kv.ActionPrompt
+import org.apache.isis.client.kroviz.ui.kv.Constants
 import org.apache.isis.client.kroviz.utils.Point
 import org.apache.isis.client.kroviz.utils.Utils
 
 class ActionDispatcher(private val at: Point = Point(100, 100)) : BaseAggregator() {
 
     override fun update(logEntry: LogEntry, subType: String) {
-        val action = logEntry.getTransferObject() as Action
-        action.links.forEach { link ->
-            if (link.isInvokeAction()) {
-                when (link.method) {
-                    Method.GET.name -> process(action, link)
-                    Method.POST.name -> invoke(action, link)
-                    Method.PUT.name -> process(action, link)
+        val to = logEntry.getTransferObject()
+        when {
+            to is Action -> {
+                to.links.forEach { link ->
+                    if (link.isInvokeAction()) {
+                        when (link.method) {
+                            Method.GET.name -> process(to, link)
+                            Method.POST.name -> invoke(to, link)
+                            Method.PUT.name -> process(to, link)
+                        }
+                    }
                 }
             }
+            (to is TObject && to.domainType == "demo.CustomUiVm") -> {
+                logEntry.aggregators.removeAt(0)
+                val oa = ObjectAggregator(to.title)
+                logEntry.aggregators.add(oa)
+                oa.update(logEntry, Constants.subTypeJson)
+            }
+            else -> {
+                console.log(to)
+                throw Throwable("[ActionDispatcher.update] ${to!!::class.simpleName}")
+            }
         }
     }
 
diff --git a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/core/aggregator/DomainTypesAggregator.kt b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/core/aggregator/DomainTypesAggregator.kt
index a23ccce..c76554a 100644
--- a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/core/aggregator/DomainTypesAggregator.kt
+++ b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/core/aggregator/DomainTypesAggregator.kt
@@ -49,9 +49,8 @@ class DomainTypesAggregator(val url: String) : BaseAggregator() {
     }
 
     private fun handleAction(obj: Action) {
-        //dsp.addData(obj)
-        console.log("[DomainTypesAggregator.handleAction] TBD: what shall we do with the action object?")
         console.log(obj)
+        throw Throwable("[DomainTypesAggregator.handleAction] not implemented yet")  //dsp.addData(obj)
     }
 
     private fun handleDomainType(obj: DomainType) {
diff --git a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/core/aggregator/ListAggregator.kt b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/core/aggregator/ListAggregator.kt
index a1f5c8a..04dd84f 100644
--- a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/core/aggregator/ListAggregator.kt
+++ b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/core/aggregator/ListAggregator.kt
@@ -42,7 +42,7 @@ class ListAggregator(actionTitle: String) : AggregatorWithLayout() {
     override fun update(logEntry: LogEntry, subType: String) {
 
         if (logEntry.state == EventState.DUPLICATE) {
-            console.log("[LA.update] TODO duplicates should not be propagated to handlers")
+            console.log("[ListAggregator.update] TODO duplicates should not be propagated to handlers")
         } else {
             when (val obj = logEntry.getTransferObject()) {
                 null -> log(logEntry)
diff --git a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/core/aggregator/ObjectAggregator.kt b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/core/aggregator/ObjectAggregator.kt
index 53b88bb..8791686 100644
--- a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/core/aggregator/ObjectAggregator.kt
+++ b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/core/aggregator/ObjectAggregator.kt
@@ -45,6 +45,7 @@ class ObjectAggregator(val actionTitle: String) : AggregatorWithLayout() {
             is TObject -> handleObject(obj)
             is ResultObject -> handleResultObject(obj)
             is Property -> handleProperty(obj)
+            is Collection -> handleCollection(obj)
             is Layout -> handleLayout(obj, dpm as ObjectDM)
             is Grid -> handleGrid(obj)
             is HttpError -> ErrorDialog(logEntry).open()
@@ -74,9 +75,13 @@ class ObjectAggregator(val actionTitle: String) : AggregatorWithLayout() {
         invoke(selfLink!!, this)
     }
 
-    fun handleResultObject(obj: ResultObject) {
-        console.log("[OA.handleResultObject] TODO implement")
+    fun handleResultObject(resultObject: ResultObject) {
+        (dpm as ObjectDM).addResult(resultObject)
+    }
+
+    fun handleCollection(obj: Collection) {
         console.log(obj)
+        throw Throwable("[ObjectAggregator.handleCollection] not implemented yet")
     }
 
     override fun getObject(): TObject? {
@@ -84,8 +89,8 @@ class ObjectAggregator(val actionTitle: String) : AggregatorWithLayout() {
     }
 
     private fun handleProperty(property: Property) {
-        console.log("[OA.handleProperty] TODO implement")
         console.log(property)
+        throw Throwable("[ObjectAggregator.handleProperty] not implemented yet")
     }
 
     private fun handleGrid(grid: Grid) {
diff --git a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/core/model/ObjectDM.kt b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/core/model/ObjectDM.kt
index 1169624..b4a97a6 100644
--- a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/core/model/ObjectDM.kt
+++ b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/core/model/ObjectDM.kt
@@ -47,6 +47,15 @@ class ObjectDM(override val title: String) : DisplayModelWithLayout() {
             val p = createPropertyFrom(m)
             addProperty(p)
         }
+        obj.getCollections().forEach { m ->
+            console.log("[ODM.addData] collection member")
+            console.log(m)
+        }
+    }
+
+    fun addResult(resultObject: ResultObject) {
+        val tObj = createObjectFrom(resultObject)
+        this.addData(tObj)
     }
 
     override fun getObject(): TObject {
@@ -94,4 +103,15 @@ class ObjectDM(override val title: String) : DisplayModelWithLayout() {
         )
     }
 
+    private fun createObjectFrom(resultObject: ResultObject): TObject {
+        val r = resultObject.result!!
+        return TObject(
+                links = r.links,
+                extensions = r.extensions!!,
+                title = r.title,
+                domainType = r.domainType,
+                instanceId = r.instanceId.toString(),
+                members = r.members)
+    }
+
 }
diff --git a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/to/bs3/Col.kt b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/to/bs3/Col.kt
index ddc5097..4d7f896 100644
--- a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/to/bs3/Col.kt
+++ b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/to/bs3/Col.kt
@@ -22,7 +22,7 @@ import org.w3c.dom.Node
 import org.w3c.dom.asList
 
 class Col(node: Node) {
-    var domainObject:DomainObject? = null
+    var domainObject: DomainObject? = null
     var actionList = mutableListOf<Action>()
     val tabGroupList = mutableListOf<TabGroup>()
     var fieldSetList = mutableListOf<FieldSet>()
@@ -36,24 +36,24 @@ class Col(node: Node) {
 
         val doNodes = nl.filter { it.nodeName.equals("cpt:domainObject") }
         if (!doNodes.isEmpty()) {
-            domainObject =DomainObject(doNodes.first())
+            domainObject = DomainObject(doNodes.first())
         }
 
         val actNodes = nl.filter { it.nodeName.equals("cpt:action") }
         for (n: Node in actNodes) {
-            val act =Action(n)
+            val act = Action(n)
             actionList.add(act)
         }
 
         val tgNodes = nl.filter { it.nodeName.equals("bs3:tabGroup") }
         for (n: Node in tgNodes) {
-            val tg =TabGroup(n)
+            val tg = TabGroup(n)
             tabGroupList.add(tg)
         }
 
         val fsNodes = nl.filter { it.nodeName.equals("cpt:fieldSet") }
         for (n: Node in fsNodes) {
-            val fs =FieldSet(n)
+            val fs = FieldSet(n)
             fieldSetList.add(fs)
         }
     }
diff --git a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/EventLogDetail.kt b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/EventLogDetail.kt
index ef2f5d2..a3b9a76 100644
--- a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/EventLogDetail.kt
+++ b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/EventLogDetail.kt
@@ -23,6 +23,7 @@ import org.apache.isis.client.kroviz.to.ValueType
 import org.apache.isis.client.kroviz.ui.kv.Constants
 import org.apache.isis.client.kroviz.ui.kv.RoDialog
 import org.apache.isis.client.kroviz.utils.Utils
+import org.apache.isis.client.kroviz.utils.XmlHelper
 
 class EventLogDetail(val logEntry: LogEntry) : Command() {
 
@@ -31,11 +32,13 @@ class EventLogDetail(val logEntry: LogEntry) : Command() {
 
         formItems.add(FormItem("Url", ValueType.TEXT, logEntry.url))
 
-        var jsonStr = logEntry.response
-        if (jsonStr.isNotEmpty() && logEntry.subType == Constants.subTypeJson) {
-            jsonStr = Utils.format(jsonStr)
+        var responseStr = logEntry.response
+        if (logEntry.subType == Constants.subTypeJson) {
+            responseStr = Utils.format(responseStr)
+        }   else {
+            responseStr = XmlHelper.formatXml(responseStr)
         }
-        formItems.add(FormItem("Response", ValueType.TEXT_AREA, jsonStr, 10))
+        formItems.add(FormItem("Response", ValueType.TEXT_AREA, responseStr, 10))
 
         var aggtStr = ""
         logEntry.aggregators.forEach { it ->
@@ -47,7 +50,7 @@ class EventLogDetail(val logEntry: LogEntry) : Command() {
                 caption = "Details :" + logEntry.title,
                 items = formItems,
                 command = this,
-                defaultAction = "Debug",
+                defaultAction = "Console",
                 widthPerc = 60).open()
     }
 
diff --git a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/builder/FieldSetBuilder.kt b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/builder/FieldSetBuilder.kt
index fa47548..27d3965 100644
--- a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/builder/FieldSetBuilder.kt
+++ b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/builder/FieldSetBuilder.kt
@@ -37,7 +37,6 @@ class FieldSetBuilder {
         console.log("[FSB.init]")
         val members = tObject.getProperties()
         val items = mutableListOf<FormItem>()
-        console.log("Members: " + members.size)
         console.log("Layout-Properties: " + fieldSetLayout.propertyList.size)
 
         for (p in fieldSetLayout.propertyList) {
diff --git a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/kv/EventLogTable.kt b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/kv/EventLogTable.kt
index ba494a9..3398df8 100644
--- a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/kv/EventLogTable.kt
+++ b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/kv/EventLogTable.kt
@@ -21,13 +21,9 @@ package org.apache.isis.client.kroviz.ui.kv
 import org.apache.isis.client.kroviz.core.event.LogEntry
 import org.apache.isis.client.kroviz.to.TObject
 import org.apache.isis.client.kroviz.ui.EventLogDetail
-import pl.treksoft.kvision.core.Border
-import pl.treksoft.kvision.core.CssSize
-import pl.treksoft.kvision.core.UNIT
+import pl.treksoft.kvision.core.*
 import pl.treksoft.kvision.html.Button
 import pl.treksoft.kvision.html.ButtonStyle
-import pl.treksoft.kvision.panel.FlexAlignItems
-import pl.treksoft.kvision.panel.FlexWrap
 import pl.treksoft.kvision.panel.VPanel
 import pl.treksoft.kvision.panel.hPanel
 import pl.treksoft.kvision.tabulator.*
@@ -111,7 +107,7 @@ class EventLogTable(val model: List<LogEntry>) : VPanel() {
 
     init {
         hPanel(FlexWrap.NOWRAP,
-                alignItems = FlexAlignItems.CENTER,
+                alignItems = AlignItems.CENTER,
                 spacing = 20) {
             border = Border(width = 1.px)
         }
diff --git a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/kv/RoDisplay.kt b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/kv/RoDisplay.kt
index c1ba4f6..aa3f833 100644
--- a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/kv/RoDisplay.kt
+++ b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/kv/RoDisplay.kt
@@ -38,6 +38,9 @@ class RoDisplay(val displayModel: ObjectDM) : Displayable, VPanel() {
         val model = displayModel.data!!
         val tObject: TObject = model.delegate
         val grid = displayModel.grid!!
+        console.log("[RD.init] tObject / grid")
+        console.log(tObject)
+        console.log(grid)
         objectPanel = LayoutBuilder().create(grid, tObject, this)
         objectPanel.width = CssSize(100, UNIT.perc)
         add(objectPanel)
diff --git a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/kv/RoIconBar.kt b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/kv/RoIconBar.kt
index c626d84..b4805a5 100644
--- a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/kv/RoIconBar.kt
+++ b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/kv/RoIconBar.kt
@@ -18,6 +18,8 @@
  */
 package org.apache.isis.client.kroviz.ui.kv
 
+import kotlinx.browser.document
+import kotlinx.dom.removeClass
 import org.apache.isis.client.kroviz.core.event.EventStore
 import org.apache.isis.client.kroviz.core.event.ResourceSpecification
 import org.apache.isis.client.kroviz.core.model.Exposer
@@ -33,8 +35,6 @@ import pl.treksoft.kvision.html.Button
 import pl.treksoft.kvision.html.ButtonStyle
 import pl.treksoft.kvision.panel.SimplePanel
 import pl.treksoft.kvision.panel.VPanel
-import kotlin.browser.document
-import kotlin.dom.removeClass
 
 object RoIconBar : SimplePanel() {
 
@@ -146,7 +146,7 @@ object RoIconBar : SimplePanel() {
         icon.id = id
         icon.title = title
         icon.addCssClass(cssClass)
-        icon.afterInsertHook = {
+        addAfterInsertHook {
             val btn = document.getElementById(btnId)!!
             btn.removeClass("dropdown-toggle")
         }
diff --git a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/kv/RoWindow.kt b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/kv/RoWindow.kt
index c190789..1f28b0c 100644
--- a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/kv/RoWindow.kt
+++ b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/ui/kv/RoWindow.kt
@@ -269,15 +269,15 @@ open class RoWindow(
                                 this@RoWindow.top = (dragStartY + (me).pageY - dragMouseY).toInt().px
                             }
                         }
-                        kotlin.browser.window.addEventListener("mousemove", moveCallback)
+                        kotlinx.browser.window.addEventListener("mousemove", moveCallback)
                         var upCallback: ((Event) -> Unit)? = null
                         upCallback = {
                             isDrag = false
                             setOpacity("1.0")
-                            kotlin.browser.window.removeEventListener("mousemove", moveCallback)
-                            kotlin.browser.window.removeEventListener("mouseup", upCallback)
+                            kotlinx.browser.window.removeEventListener("mousemove", moveCallback)
+                            kotlinx.browser.window.removeEventListener("mouseup", upCallback)
                         }
-                        kotlin.browser.window.addEventListener("mouseup", upCallback)
+                        kotlinx.browser.window.addEventListener("mouseup", upCallback)
                     }
                 }
             }
@@ -416,38 +416,3 @@ open class RoWindow(
     }
 }
 
-/**
- * DSL builder extension function.
- *
- * It takes the same parameters as the constructor of the built component.
- */
-fun Container.window(
-        caption: String? = null,
-        contentWidth: CssSize? = CssSize(0, UNIT.auto),
-        contentHeight: CssSize? = CssSize(0, UNIT.auto),
-        isResizable: Boolean = true,
-        isDraggable: Boolean = true,
-        closeButton: Boolean = false,
-        maximizeButton: Boolean = false,
-        minimizeButton: Boolean = false,
-        icon: String? = null,
-        classes: Set<String> = setOf(),
-        init: (RoWindow.() -> Unit)? = null
-): RoWindow {
-    val window =
-            RoWindow(
-                    caption,
-                    contentWidth,
-                    contentHeight,
-                    isResizable,
-                    isDraggable,
-                    closeButton,
-                    maximizeButton,
-                    minimizeButton,
-                    icon,
-                    classes,
-                    init
-            )
-    this.add(window)
-    return window
-}
diff --git a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/utils/DomUtil.kt b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/utils/DomUtil.kt
index 0548604..2d000c9 100644
--- a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/utils/DomUtil.kt
+++ b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/utils/DomUtil.kt
@@ -20,7 +20,7 @@ package org.apache.isis.client.kroviz.utils
 
 import org.w3c.dom.Document
 import org.w3c.dom.Element
-import kotlin.browser.document
+import kotlinx.browser.document
 
 external fun encodeURIComponent(encodedURI: String): String
 
diff --git a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/utils/IconManager.kt b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/utils/IconManager.kt
index 35dfa89..84f401a 100644
--- a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/utils/IconManager.kt
+++ b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/utils/IconManager.kt
@@ -39,6 +39,7 @@ object IconManager {
             "Close" to "times",
             "Configuration" to "wrench",
             "Connect" to "plug",
+            "Console" to "terminal",
             "Create" to "plus",
             "Debug" to "bug",
             "Delete" to "trash",
diff --git a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/utils/XmlHelper.kt b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/utils/XmlHelper.kt
index 490e630..0dd8fe8 100644
--- a/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/utils/XmlHelper.kt
+++ b/incubator/clients/kroviz/src/main/kotlin/org/apache/isis/client/kroviz/utils/XmlHelper.kt
@@ -47,4 +47,29 @@ object XmlHelper {
         return p.parseFromString(xmlStr, "application/xml")
     }
 
+    // Adopted from @link https://stackoverflow.com/questions/376373/pretty-printing-xml-with-javascript
+    fun formatXml(xml: String): String {
+        var formatted = ""
+        val reg = "/(>)(<)(/*)/g"
+        var pad = 0
+        xml.split(reg).forEach { node ->
+            var indent = 0
+            when {
+                "/.+</w[^>]*>$/".toRegex().containsMatchIn(node) -> indent = 0
+                "/^</w/".toRegex().containsMatchIn(node) -> {
+                    if (pad != 0) pad -= 1
+                }
+                "/^<w[^>]*[^/]>.*$/".toRegex().containsMatchIn(node) -> indent = 1
+                else -> indent = 0
+            }
+
+            var padding = ""
+            (1..pad).forEach { padding += "  " }
+
+            formatted += padding + node + "\r\n"
+            pad += indent
+        }
+        return formatted.substring(1, formatted.length - 3);
+    }
+
 }
diff --git a/incubator/clients/kroviz/src/main/resources/img/claever.svg b/incubator/clients/kroviz/src/main/resources/img/claever.svg
index be4826f..6f98c09 100644
--- a/incubator/clients/kroviz/src/main/resources/img/claever.svg
+++ b/incubator/clients/kroviz/src/main/resources/img/claever.svg
@@ -60,40 +60,41 @@
      inkscape:groupmode="layer"
      id="layer1"
      transform="translate(81.788246,-188.44201)">
+    <!-- red-form, but color is set at the very end  blue:3c75bc /-->
     <g
        id="g2834"
        transform="translate(4.2180348,-244.97944)"
-       style="fill:#339900">
+       style="fill:#c03d3a">
       <rect
+         style="fill-opacity:0.7;fill-rule:nonzero;stroke:none"
          transform="rotate(45)"
          y="128.81105"
          x="540.95331"
          height="228.57143"
          width="228.57143"
-         id="rect2816"
-         style="fill:#339900;fill-opacity:1;fill-rule:nonzero;stroke:none" />
+         id="rect2816" />
       <circle
+         style="fill-opacity:0.7;fill-rule:nonzero;stroke:none"
          transform="translate(-81.952479,151.13084)"
          id="path2820"
-         style="fill:#339900;fill-opacity:1;fill-rule:nonzero;stroke:none"
          cx="294.28571"
          cy="400.93362"
          r="117.14286" />
       <circle
-         style="fill:#339900;fill-opacity:1;fill-rule:nonzero;stroke:none"
+         style="fill-opacity:0.7;fill-rule:nonzero;stroke:none"
          id="path2822"
          transform="translate(77.738331,149.63069)"
          cx="294.28571"
          cy="400.93362"
          r="117.14286" />
     </g>
-    <!-- green -->
+    <!-- green 94ba46 / 9BBB59-->
     <g
        transform="matrix(1,0,0,-1,4.2180347,1378.2333)"
        id="g2839"
-       style="fill:#9BBB59">
+       style="fill:#94ba46">
       <rect
-         style="fill:#9BBB59;fill-opacity:1;fill-rule:nonzero;stroke:none"
+         style="fill-opacity:0.8;fill-rule:nonzero;stroke:none"
          id="rect2841"
          width="228.57143"
          height="228.57143"
@@ -101,27 +102,27 @@
          y="128.81105"
          transform="rotate(45)" />
       <circle
-         style="fill:#9BBB59;fill-opacity:1;fill-rule:nonzero;stroke:none"
+         style="fill-opacity:0.8;fill-rule:nonzero;stroke:none"
          id="path2843"
          transform="translate(-81.952479,151.13084)"
          cx="294.28571"
          cy="400.93362"
          r="117.14286" />
       <circle
+         style="fill-opacity:0.8;fill-rule:nonzero;stroke:none"
          transform="translate(77.738331,149.63069)"
          id="path2845"
-         style="fill:#9BBB59;fill-opacity:1;fill-rule:nonzero;stroke:none"
          cx="294.28571"
          cy="400.93362"
          r="117.14286" />
     </g>
-    <!-- yellow -->
+    <!-- yellow / F79646 -->
     <g
        transform="rotate(90,416.77735,691.22564)"
        id="g2859"
        style="fill:#F79646">
       <rect
-         style="fill:#F79646;fill-opacity:1;fill-rule:nonzero;stroke:none"
+         style="fill-opacity:0.8;fill-rule:nonzero;stroke:none"
          id="rect2861"
          width="228.57143"
          height="228.57143"
@@ -129,16 +130,16 @@
          y="128.81105"
          transform="rotate(45)" />
       <circle
-         style="fill:#F79646;fill-opacity:1;fill-rule:nonzero;stroke:none"
+         style="fill-opacity:0.8;fill-rule:nonzero;stroke:none"
          id="path2863"
          transform="translate(-81.952479,151.13084)"
          cx="294.28571"
          cy="400.93362"
          r="117.14286" />
       <circle
+         style="fill-opacity:0.8;fill-rule:nonzero;stroke:none"
          transform="translate(77.738331,149.63069)"
          id="path2865"
-         style="fill:#F79646;fill-opacity:1;fill-rule:nonzero;stroke:none"
          cx="294.28571"
          cy="400.93362"
          r="117.14286" />
@@ -149,30 +150,31 @@
        transform="matrix(0,1,1,0,-515.2097,274.44829)"
        style="fill:#4F81BD">
       <rect
+         style="fill-opacity:0.8;fill-rule:nonzero;stroke:none"
          transform="rotate(45)"
          y="128.81105"
          x="540.95331"
          height="228.57143"
          width="228.57143"
-         id="rect2869"
-         style="fill:#4F81BD;fill-opacity:1;fill-rule:nonzero;stroke:none" />
+         id="rect2869" />
       <circle
+         style="fill-opacity:0.8;fill-rule:nonzero;stroke:none"
          transform="translate(-81.952479,151.13084)"
          id="path2871"
-         style="fill:#4F81BD;fill-opacity:1;fill-rule:nonzero;stroke:none"
          cx="294.28571"
          cy="400.93362"
          r="117.14286" />
       <circle
-         style="fill:#4F81BD;fill-opacity:1;fill-rule:nonzero;stroke:none"
+         style="fill-opacity:0.8;fill-rule:nonzero;stroke:none"
          id="path2873"
          transform="translate(77.738331,149.63069)"
          cx="294.28571"
          cy="400.93362"
          r="117.14286" />
     </g>
+    <!-- red c03d3a / C0504D-->
     <path
-       style="fill:#C0504D;fill-opacity:1;stroke-width:2.10894227"
+       style="fill:#c03d3a;fill-opacity:0.4;stroke-width:2.10894227"
        d="m 292.44409,276.24527 c -46.08028,-46.09678 -85.57389,-86.1849 -87.76358,-89.0847 -13.6553,-18.08364 -21.56331,-43.21984 -21.56331,-68.54062 0,-50.917837 30.6962,-93.441035 79.2077,-109.7258122 35.18508,-11.8112416 76.41069,-5.2901011 104.93851,16.5993602 l 10.01748,7.686432 9.83234,-7.5052 c 46.81235,-35.732676 111.3974,-31.6665489 152.55621,9.604593 22.42968,22.490876 33.67123,49.601489 33.71804,81.315837 0.0306,20.77227 -4.06498,37.22126 -13.95305,56.0383 -6.5524,12.46921 -1 [...]
        id="path829"
        inkscape:connector-curvature="0"
diff --git a/incubator/clients/kroviz/src/test/kotlin/org/apache/isis/client/kroviz/snapshots/ResponseRegressionTest.kt b/incubator/clients/kroviz/src/test/kotlin/org/apache/isis/client/kroviz/snapshots/ResponseRegressionTest.kt
index 50679b9..8548fbc 100644
--- a/incubator/clients/kroviz/src/test/kotlin/org/apache/isis/client/kroviz/snapshots/ResponseRegressionTest.kt
+++ b/incubator/clients/kroviz/src/test/kotlin/org/apache/isis/client/kroviz/snapshots/ResponseRegressionTest.kt
@@ -18,11 +18,12 @@
  */
 package org.apache.isis.client.kroviz.snapshots
 
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import org.apache.isis.client.kroviz.snapshots.demo2_0_0.Response2Handler
 import org.apache.isis.client.kroviz.to.Link
 import org.apache.isis.client.kroviz.to.Method
 import org.apache.isis.client.kroviz.ui.kv.UiManager
-import pl.treksoft.kvision.require
+import org.w3c.workers.Client
 import kotlin.test.BeforeTest
 import kotlin.test.Test
 import kotlin.test.assertEquals
@@ -32,19 +33,20 @@ import kotlin.test.assertTrue
  * This is an integration test that requires <Demo> running on http://localhost:8080
  * automate via -> @link https://bmuschko.com/blog/docker-integration-testing/
  * compare json -> @link https://stackoverflow.com/questions/26049303/how-to-compare-two-json-have-the-same-properties-without-order
+ * eventually use HttpClient? -> @link https://blog.kotlin-academy.com/how-to-create-a-rest-api-client-and-its-integration-tests-in-kotlin-multiplatform-d76c9a1be348
  */
 class ResponseRegressionTest {
 
     @BeforeTest
     fun setup() {
-//        require("xmlhttprequest").XmlHttpRequest;
         val user = "sven"
         val pw = "pass"
         val url = "http://${user}:${pw}@localhost:8080/restful/"
         UiManager.login(url, user, pw)
     }
 
-    //@Test  // invoking HttpRequest does not work - yet
+    @ExperimentalCoroutinesApi
+    @Test
     fun testCompareSnapshotWithResponse() {
         //given
         val map = Response2Handler.map
@@ -52,6 +54,7 @@ class ResponseRegressionTest {
         //when
         console.log("[RRT.testCompareSnapshotWithResponse]")
         map.forEach { rh ->
+
             val handler = rh.value
             val jsonStr = rh.key.str
             val expected = handler.parse(jsonStr)
@@ -60,14 +63,25 @@ class ResponseRegressionTest {
             console.log(href)
 
             val link = Link(method = Method.GET.operation, href = href)
-            val response = SyncRequest().invoke(link, credentials)
-
+            val response = invoke(link, credentials)
             val actual = handler.parse(response)
-
             assertEquals(expected, actual)
+            console.log("[RRT.testCompareSnapshotWithResponse]")
+            console.log(expected == actual)
+
         }
         //then
         assertTrue(true, "no exception in loop")
     }
 
+    @ExperimentalCoroutinesApi
+    private fun invoke(link: Link, credentials: String): String {
+        return TestRequest().fetch(link, credentials)
+    }
+
+    private fun invokeSync(link: Link, credentials: String): String {
+      //  val hc = Client("id", link.href)
+        return "Why must this be so complicated?"
+    }
+
 }
diff --git a/incubator/clients/kroviz/src/test/kotlin/org/apache/isis/client/kroviz/snapshots/SyncRequest.kt b/incubator/clients/kroviz/src/test/kotlin/org/apache/isis/client/kroviz/snapshots/SyncRequest.kt
deleted file mode 100644
index 38859ec..0000000
--- a/incubator/clients/kroviz/src/test/kotlin/org/apache/isis/client/kroviz/snapshots/SyncRequest.kt
+++ /dev/null
@@ -1,27 +0,0 @@
-package org.apache.isis.client.kroviz.snapshots
-
-import org.apache.isis.client.kroviz.IntegrationTest
-import org.apache.isis.client.kroviz.to.Link
-import org.apache.isis.client.kroviz.ui.kv.Constants
-import org.w3c.xhr.XMLHttpRequest
-
-class SyncRequest : IntegrationTest() {
-
-    fun invoke(link: Link, credentials: String): String {
-        val method = link.method
-        val url = link.href
-
-        val xhr = XMLHttpRequest()
-        xhr.open(method, url, false)
-        xhr.setRequestHeader("Authorization", "Basic $credentials")
-        xhr.setRequestHeader("Content-Type", Constants.stdMimeType)
-        xhr.setRequestHeader("Accept", Constants.svgMimeType)
-
-        xhr.send()
-        while (xhr.readyState != XMLHttpRequest.DONE) {
-            wait(100)
-            console.log("[SyncRequest.invoke] wait")
-        }
-        return (xhr.responseText);
-    }
-}
diff --git a/incubator/clients/kroviz/src/test/kotlin/org/apache/isis/client/kroviz/snapshots/TestRequest.kt b/incubator/clients/kroviz/src/test/kotlin/org/apache/isis/client/kroviz/snapshots/TestRequest.kt
new file mode 100644
index 0000000..5bdf293
--- /dev/null
+++ b/incubator/clients/kroviz/src/test/kotlin/org/apache/isis/client/kroviz/snapshots/TestRequest.kt
@@ -0,0 +1,36 @@
+package org.apache.isis.client.kroviz.snapshots
+
+import kotlinx.browser.window
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.asDeferred
+import kotlinx.coroutines.await
+import org.apache.isis.client.kroviz.IntegrationTest
+import org.apache.isis.client.kroviz.to.Link
+import org.apache.isis.client.kroviz.ui.kv.Constants
+import org.w3c.fetch.Response
+import kotlin.js.Promise
+
+class TestRequest : IntegrationTest() {
+
+    // see example at @link https://play.kotlinlang.org/hands-on/Building%20Web%20Applications%20with%20React%20and%20Kotlin%20JS/08_Using_an_External_REST_API
+    @ExperimentalCoroutinesApi
+    suspend fun fetch(link: Link, credentials: String): String {
+        val subType = Constants.subTypeJson
+
+        val header: dynamic = object {}
+        header["Authorization"] = "Basic $credentials"
+        header["Content-Type"] = "application/$subType;charset=UTF-8"
+        header["Accept"] = "application/$subType"
+
+        val init: dynamic = object {}
+        init["method"] = link.method
+        init["header"] = header
+
+        val responsePromise: Promise<Response> = window.fetch(link.href, init)
+        val response = responsePromise.await()
+        val jsonPromise = response.json()
+        val json = jsonPromise.await()
+        return json.unsafeCast<String>()
+    }
+
+}
diff --git a/incubator/clients/kroviz/src/test/kotlin/org/apache/isis/client/kroviz/snapshots/demo2_0_0/ACTIONS_WHEREINTHEWORLD_INVOKE.kt b/incubator/clients/kroviz/src/test/kotlin/org/apache/isis/client/kroviz/snapshots/demo2_0_0/ACTIONS_WHEREINTHEWORLD_INVOKE.kt
new file mode 100644
index 0000000..5844700
--- /dev/null
+++ b/incubator/clients/kroviz/src/test/kotlin/org/apache/isis/client/kroviz/snapshots/demo2_0_0/ACTIONS_WHEREINTHEWORLD_INVOKE.kt
@@ -0,0 +1,198 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.client.kroviz.snapshots.demo2_0_0
+
+import org.apache.isis.client.kroviz.snapshots.Response
+
+object ACTIONS_WHEREINTHEWORLD_INVOKE : Response() {
+    override val url = "http://localhost:8080/restful/objects/demo.WhereInTheWorldMenu/1/actions/whereInTheWorld/invoke?address=Malvern,%20UK&zoom=14"
+    override val str = """
+{
+  "links" : [ {
+    "rel" : "self",
+    "href" : "http://localhost:8080/restful/objects/demo.CustomUiVm/PADw_eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04IiBzdGFuZGFsb25lPSJ5ZXMiPz4KPGRlbW8uQ3VzdG9tVWlWbT4KICAgIDxhZGRyZXNzPk1hbHZlcm4sIFVLPC9hZGRyZXNzPgogICAgPGxhdGl0dWRlPjUyLjE5ODk0NDwvbGF0aXR1ZGU-CiAgICA8bG9uZ2l0dWRlPi0yLjI0MjY1NzEwNzkxMzA4ODY8L2xvbmdpdHVkZT4KICAgIDx6b29tPjE0PC96b29tPgo8L2RlbW8uQ3VzdG9tVWlWbT4K",
+    "method" : "GET",
+    "type" : "application/json;profile=\"urn:org.restfulobjects:repr-types/object\"",
+    "title" : "Malvern, UK"
+  }, {
+    "rel" : "describedby",
+    "href" : "http://localhost:8080/restful/domain-types/demo.CustomUiVm",
+    "method" : "GET",
+    "type" : "application/json;profile=\"urn:org.restfulobjects:repr-types/domain-type\""
+  }, {
+    "rel" : "urn:org.apache.isis.restfulobjects:rels/object-layout",
+    "href" : "http://localhost:8080/restful/objects/demo.CustomUiVm/PADw_eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04IiBzdGFuZGFsb25lPSJ5ZXMiPz4KPGRlbW8uQ3VzdG9tVWlWbT4KICAgIDxhZGRyZXNzPk1hbHZlcm4sIFVLPC9hZGRyZXNzPgogICAgPGxhdGl0dWRlPjUyLjE5ODk0NDwvbGF0aXR1ZGU-CiAgICA8bG9uZ2l0dWRlPi0yLjI0MjY1NzEwNzkxMzA4ODY8L2xvbmdpdHVkZT4KICAgIDx6b29tPjE0PC96b29tPgo8L2RlbW8uQ3VzdG9tVWlWbT4K/object-layout",
+    "method" : "GET",
+    "type" : "application/json;profile=\"urn:org.restfulobjects:repr-types/object-layout-bs3\""
+  }, {
+    "rel" : "urn:org.apache.isis.restfulobjects:rels/object-icon",
+    "href" : "http://localhost:8080/restful/objects/demo.CustomUiVm/PADw_eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04IiBzdGFuZGFsb25lPSJ5ZXMiPz4KPGRlbW8uQ3VzdG9tVWlWbT4KICAgIDxhZGRyZXNzPk1hbHZlcm4sIFVLPC9hZGRyZXNzPgogICAgPGxhdGl0dWRlPjUyLjE5ODk0NDwvbGF0aXR1ZGU-CiAgICA8bG9uZ2l0dWRlPi0yLjI0MjY1NzEwNzkxMzA4ODY8L2xvbmdpdHVkZT4KICAgIDx6b29tPjE0PC96b29tPgo8L2RlbW8uQ3VzdG9tVWlWbT4K/image",
+    "method" : "GET",
+    "type" : "image/png"
+  }, {
+    "rel" : "urn:org.restfulobjects:rels/update",
+    "href" : "http://localhost:8080/restful/objects/demo.CustomUiVm:PADw_eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04IiBzdGFuZGFsb25lPSJ5ZXMiPz4KPGRlbW8uQ3VzdG9tVWlWbT4KICAgIDxhZGRyZXNzPk1hbHZlcm4sIFVLPC9hZGRyZXNzPgogICAgPGxhdGl0dWRlPjUyLjE5ODk0NDwvbGF0aXR1ZGU-CiAgICA8bG9uZ2l0dWRlPi0yLjI0MjY1NzEwNzkxMzA4ODY8L2xvbmdpdHVkZT4KICAgIDx6b29tPjE0PC96b29tPgo8L2RlbW8uQ3VzdG9tVWlWbT4K",
+    "method" : "PUT",
+    "type" : "application/json;profile=\"urn:org.restfulobjects:repr-types/object\"",
+    "arguments" : { }
+  } ],
+  "extensions" : {
+    "oid" : "demo.CustomUiVm:PADw_eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04IiBzdGFuZGFsb25lPSJ5ZXMiPz4KPGRlbW8uQ3VzdG9tVWlWbT4KICAgIDxhZGRyZXNzPk1hbHZlcm4sIFVLPC9hZGRyZXNzPgogICAgPGxhdGl0dWRlPjUyLjE5ODk0NDwvbGF0aXR1ZGU-CiAgICA8bG9uZ2l0dWRlPi0yLjI0MjY1NzEwNzkxMzA4ODY8L2xvbmdpdHVkZT4KICAgIDx6b29tPjE0PC96b29tPgo8L2RlbW8uQ3VzdG9tVWlWbT4K",
+    "isService" : false,
+    "isPersistent" : true
+  },
+  "title" : "Malvern, UK",
+  "domainType" : "demo.CustomUiVm",
+  "instanceId" : "PADw_eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04IiBzdGFuZGFsb25lPSJ5ZXMiPz4KPGRlbW8uQ3VzdG9tVWlWbT4KICAgIDxhZGRyZXNzPk1hbHZlcm4sIFVLPC9hZGRyZXNzPgogICAgPGxhdGl0dWRlPjUyLjE5ODk0NDwvbGF0aXR1ZGU-CiAgICA8bG9uZ2l0dWRlPi0yLjI0MjY1NzEwNzkxMzA4ODY8L2xvbmdpdHVkZT4KICAgIDx6b29tPjE0PC96b29tPgo8L2RlbW8uQ3VzdG9tVWlWbT4K",
+  "members" : {
+    "address" : {
+      "id" : "address",
+      "memberType" : "property",
+      "links" : [ {
+        "rel" : "urn:org.restfulobjects:rels/details;property=\"address\"",
+        "href" : "http://localhost:8080/restful/objects/demo.CustomUiVm/PADw_eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04IiBzdGFuZGFsb25lPSJ5ZXMiPz4KPGRlbW8uQ3VzdG9tVWlWbT4KICAgIDxhZGRyZXNzPk1hbHZlcm4sIFVLPC9hZGRyZXNzPgogICAgPGxhdGl0dWRlPjUyLjE5ODk0NDwvbGF0aXR1ZGU-CiAgICA8bG9uZ2l0dWRlPi0yLjI0MjY1NzEwNzkxMzA4ODY8L2xvbmdpdHVkZT4KICAgIDx6b29tPjE0PC96b29tPgo8L2RlbW8uQ3VzdG9tVWlWbT4K/properties/address",
+        "method" : "GET",
+        "type" : "application/json;profile=\"urn:org.restfulobjects:repr-types/object-property\""
+      } ],
+      "value" : "Malvern, UK",
+      "extensions" : {
+        "x-isis-format" : "string"
+      },
+      "disabledReason" : "Disabled"
+    },
+    "latitude" : {
+      "id" : "latitude",
+      "memberType" : "property",
+      "links" : [ {
+        "rel" : "urn:org.restfulobjects:rels/details;property=\"latitude\"",
+        "href" : "http://localhost:8080/restful/objects/demo.CustomUiVm/PADw_eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04IiBzdGFuZGFsb25lPSJ5ZXMiPz4KPGRlbW8uQ3VzdG9tVWlWbT4KICAgIDxhZGRyZXNzPk1hbHZlcm4sIFVLPC9hZGRyZXNzPgogICAgPGxhdGl0dWRlPjUyLjE5ODk0NDwvbGF0aXR1ZGU-CiAgICA8bG9uZ2l0dWRlPi0yLjI0MjY1NzEwNzkxMzA4ODY8L2xvbmdpdHVkZT4KICAgIDx6b29tPjE0PC96b29tPgo8L2RlbW8uQ3VzdG9tVWlWbT4K/properties/latitude",
+        "method" : "GET",
+        "type" : "application/json;profile=\"urn:org.restfulobjects:repr-types/object-property\""
+      } ],
+      "value" : "52.198944",
+      "extensions" : {
+        "x-isis-format" : "string"
+      },
+      "disabledReason" : "Disabled"
+    },
+    "longitude" : {
+      "id" : "longitude",
+      "memberType" : "property",
+      "links" : [ {
+        "rel" : "urn:org.restfulobjects:rels/details;property=\"longitude\"",
+        "href" : "http://localhost:8080/restful/objects/demo.CustomUiVm/PADw_eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04IiBzdGFuZGFsb25lPSJ5ZXMiPz4KPGRlbW8uQ3VzdG9tVWlWbT4KICAgIDxhZGRyZXNzPk1hbHZlcm4sIFVLPC9hZGRyZXNzPgogICAgPGxhdGl0dWRlPjUyLjE5ODk0NDwvbGF0aXR1ZGU-CiAgICA8bG9uZ2l0dWRlPi0yLjI0MjY1NzEwNzkxMzA4ODY8L2xvbmdpdHVkZT4KICAgIDx6b29tPjE0PC96b29tPgo8L2RlbW8uQ3VzdG9tVWlWbT4K/properties/longitude",
+        "method" : "GET",
+        "type" : "application/json;profile=\"urn:org.restfulobjects:repr-types/object-property\""
+      } ],
+      "value" : "-2.2426571079130886",
+      "extensions" : {
+        "x-isis-format" : "string"
+      },
+      "disabledReason" : "Disabled"
+    },
+    "zoom" : {
+      "id" : "zoom",
+      "memberType" : "property",
+      "links" : [ {
+        "rel" : "urn:org.restfulobjects:rels/details;property=\"zoom\"",
+        "href" : "http://localhost:8080/restful/objects/demo.CustomUiVm/PADw_eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04IiBzdGFuZGFsb25lPSJ5ZXMiPz4KPGRlbW8uQ3VzdG9tVWlWbT4KICAgIDxhZGRyZXNzPk1hbHZlcm4sIFVLPC9hZGRyZXNzPgogICAgPGxhdGl0dWRlPjUyLjE5ODk0NDwvbGF0aXR1ZGU-CiAgICA8bG9uZ2l0dWRlPi0yLjI0MjY1NzEwNzkxMzA4ODY8L2xvbmdpdHVkZT4KICAgIDx6b29tPjE0PC96b29tPgo8L2RlbW8uQ3VzdG9tVWlWbT4K/properties/zoom",
+        "method" : "GET",
+        "type" : "application/json;profile=\"urn:org.restfulobjects:repr-types/object-property\""
+      } ],
+      "value" : 14,
+      "format" : "int",
+      "extensions" : {
+        "x-isis-format" : "int"
+      },
+      "disabledReason" : "Disabled"
+    },
+    "clearHints" : {
+      "id" : "clearHints",
+      "memberType" : "action",
+      "links" : [ {
+        "rel" : "urn:org.restfulobjects:rels/details;action=\"clearHints\"",
+        "href" : "http://localhost:8080/restful/objects/demo.CustomUiVm/PADw_eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04IiBzdGFuZGFsb25lPSJ5ZXMiPz4KPGRlbW8uQ3VzdG9tVWlWbT4KICAgIDxhZGRyZXNzPk1hbHZlcm4sIFVLPC9hZGRyZXNzPgogICAgPGxhdGl0dWRlPjUyLjE5ODk0NDwvbGF0aXR1ZGU-CiAgICA8bG9uZ2l0dWRlPi0yLjI0MjY1NzEwNzkxMzA4ODY8L2xvbmdpdHVkZT4KICAgIDx6b29tPjE0PC96b29tPgo8L2RlbW8uQ3VzdG9tVWlWbT4K/actions/clearHints",
+        "method" : "GET",
+        "type" : "application/json;profile=\"urn:org.restfulobjects:repr-types/object-action\""
+      } ]
+    },
+    "inspectMetamodel" : {
+      "id" : "inspectMetamodel",
+      "memberType" : "action",
+      "links" : [ {
+        "rel" : "urn:org.restfulobjects:rels/details;action=\"inspectMetamodel\"",
+        "href" : "http://localhost:8080/restful/objects/demo.CustomUiVm/PADw_eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04IiBzdGFuZGFsb25lPSJ5ZXMiPz4KPGRlbW8uQ3VzdG9tVWlWbT4KICAgIDxhZGRyZXNzPk1hbHZlcm4sIFVLPC9hZGRyZXNzPgogICAgPGxhdGl0dWRlPjUyLjE5ODk0NDwvbGF0aXR1ZGU-CiAgICA8bG9uZ2l0dWRlPi0yLjI0MjY1NzEwNzkxMzA4ODY8L2xvbmdpdHVkZT4KICAgIDx6b29tPjE0PC96b29tPgo8L2RlbW8uQ3VzdG9tVWlWbT4K/actions/inspectMetamodel",
+        "method" : "GET",
+        "type" : "application/json;profile=\"urn:org.restfulobjects:repr-types/object-action\""
+      } ]
+    },
+    "downloadLayoutXml" : {
+      "id" : "downloadLayoutXml",
+      "memberType" : "action",
+      "links" : [ {
+        "rel" : "urn:org.restfulobjects:rels/details;action=\"downloadLayoutXml\"",
+        "href" : "http://localhost:8080/restful/objects/demo.CustomUiVm/PADw_eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04IiBzdGFuZGFsb25lPSJ5ZXMiPz4KPGRlbW8uQ3VzdG9tVWlWbT4KICAgIDxhZGRyZXNzPk1hbHZlcm4sIFVLPC9hZGRyZXNzPgogICAgPGxhdGl0dWRlPjUyLjE5ODk0NDwvbGF0aXR1ZGU-CiAgICA8bG9uZ2l0dWRlPi0yLjI0MjY1NzEwNzkxMzA4ODY8L2xvbmdpdHVkZT4KICAgIDx6b29tPjE0PC96b29tPgo8L2RlbW8uQ3VzdG9tVWlWbT4K/actions/downloadLayoutXml",
+        "method" : "GET",
+        "type" : "application/json;profile=\"urn:org.restfulobjects:repr-types/object-action\""
+      } ]
+    },
+    "recentCommands" : {
+      "id" : "recentCommands",
+      "memberType" : "action",
+      "links" : [ {
+        "rel" : "urn:org.restfulobjects:rels/details;action=\"recentCommands\"",
+        "href" : "http://localhost:8080/restful/objects/demo.CustomUiVm/PADw_eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04IiBzdGFuZGFsb25lPSJ5ZXMiPz4KPGRlbW8uQ3VzdG9tVWlWbT4KICAgIDxhZGRyZXNzPk1hbHZlcm4sIFVLPC9hZGRyZXNzPgogICAgPGxhdGl0dWRlPjUyLjE5ODk0NDwvbGF0aXR1ZGU-CiAgICA8bG9uZ2l0dWRlPi0yLjI0MjY1NzEwNzkxMzA4ODY8L2xvbmdpdHVkZT4KICAgIDx6b29tPjE0PC96b29tPgo8L2RlbW8uQ3VzdG9tVWlWbT4K/actions/recentCommands",
+        "method" : "GET",
+        "type" : "application/json;profile=\"urn:org.restfulobjects:repr-types/object-action\""
+      } ]
+    },
+    "rebuildMetamodel" : {
+      "id" : "rebuildMetamodel",
+      "memberType" : "action",
+      "links" : [ {
+        "rel" : "urn:org.restfulobjects:rels/details;action=\"rebuildMetamodel\"",
+        "href" : "http://localhost:8080/restful/objects/demo.CustomUiVm/PADw_eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04IiBzdGFuZGFsb25lPSJ5ZXMiPz4KPGRlbW8uQ3VzdG9tVWlWbT4KICAgIDxhZGRyZXNzPk1hbHZlcm4sIFVLPC9hZGRyZXNzPgogICAgPGxhdGl0dWRlPjUyLjE5ODk0NDwvbGF0aXR1ZGU-CiAgICA8bG9uZ2l0dWRlPi0yLjI0MjY1NzEwNzkxMzA4ODY8L2xvbmdpdHVkZT4KICAgIDx6b29tPjE0PC96b29tPgo8L2RlbW8uQ3VzdG9tVWlWbT4K/actions/rebuildMetamodel",
+        "method" : "GET",
+        "type" : "application/json;profile=\"urn:org.restfulobjects:repr-types/object-action\""
+      } ]
+    },
+    "downloadMetamodelXml" : {
+      "id" : "downloadMetamodelXml",
+      "memberType" : "action",
+      "links" : [ {
+        "rel" : "urn:org.restfulobjects:rels/details;action=\"downloadMetamodelXml\"",
+        "href" : "http://localhost:8080/restful/objects/demo.CustomUiVm/PADw_eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04IiBzdGFuZGFsb25lPSJ5ZXMiPz4KPGRlbW8uQ3VzdG9tVWlWbT4KICAgIDxhZGRyZXNzPk1hbHZlcm4sIFVLPC9hZGRyZXNzPgogICAgPGxhdGl0dWRlPjUyLjE5ODk0NDwvbGF0aXR1ZGU-CiAgICA8bG9uZ2l0dWRlPi0yLjI0MjY1NzEwNzkxMzA4ODY8L2xvbmdpdHVkZT4KICAgIDx6b29tPjE0PC96b29tPgo8L2RlbW8uQ3VzdG9tVWlWbT4K/actions/downloadMetamodelXml",
+        "method" : "GET",
+        "type" : "application/json;profile=\"urn:org.restfulobjects:repr-types/object-action\""
+      } ]
+    },
+    "openRestApi" : {
+      "id" : "openRestApi",
+      "memberType" : "action",
+      "links" : [ {
+        "rel" : "urn:org.restfulobjects:rels/details;action=\"openRestApi\"",
+        "href" : "http://localhost:8080/restful/objects/demo.CustomUiVm/PADw_eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04IiBzdGFuZGFsb25lPSJ5ZXMiPz4KPGRlbW8uQ3VzdG9tVWlWbT4KICAgIDxhZGRyZXNzPk1hbHZlcm4sIFVLPC9hZGRyZXNzPgogICAgPGxhdGl0dWRlPjUyLjE5ODk0NDwvbGF0aXR1ZGU-CiAgICA8bG9uZ2l0dWRlPi0yLjI0MjY1NzEwNzkxMzA4ODY8L2xvbmdpdHVkZT4KICAgIDx6b29tPjE0PC96b29tPgo8L2RlbW8uQ3VzdG9tVWlWbT4K/actions/openRestApi",
+        "method" : "GET",
+        "type" : "application/json;profile=\"urn:org.restfulobjects:repr-types/object-action\""
+      } ]
+    }
+  }
+}
+"""
+}
diff --git a/incubator/clients/kroviz/src/test/kotlin/org/apache/isis/client/kroviz/snapshots/demo2_0_0/Response2Handler.kt b/incubator/clients/kroviz/src/test/kotlin/org/apache/isis/client/kroviz/snapshots/demo2_0_0/Response2Handler.kt
index 84cb1f5..0bea8b0 100644
--- a/incubator/clients/kroviz/src/test/kotlin/org/apache/isis/client/kroviz/snapshots/demo2_0_0/Response2Handler.kt
+++ b/incubator/clients/kroviz/src/test/kotlin/org/apache/isis/client/kroviz/snapshots/demo2_0_0/Response2Handler.kt
@@ -23,9 +23,9 @@ import org.apache.isis.client.kroviz.handler.*
 object Response2Handler {
 
     val map = mapOf(
-            RESTFUL to RestfulHandler(),
             ACTIONS_STRINGS to ActionHandler(),
             ACTIONS_STRINGS_INVOKE to TObjectHandler(),
+            ACTIONS_WHEREINTHEWORLD_INVOKE to TObjectHandler(),
             ACTIONS_TEXT_INVOKE to TObjectHandler(),
             ASSOCIATED_ACTION_OBJECT_LAYOUT to LayoutHandler(),
             COLLECTIONS_ENTITIES to CollectionHandler(),
@@ -38,6 +38,7 @@ object Response2Handler {
             PRIMITIVES to TObjectHandler(),
             PROPERTY to PropertyHandler(),
             PROPERTY_DESCRIPTION to PropertyHandler(),
+            RESTFUL to RestfulHandler(),
             RESTFUL_DOMAIN_TYPES to DomainTypesHandler(),
             TAB_OBJECT_LAYOUT to LayoutHandler(),
             TAB_LAYOUT_XML to LayoutXmlHandler(),
diff --git a/incubator/clients/kroviz/src/test/kotlin/org/apache/isis/client/kroviz/snapshots/sample.json b/incubator/clients/kroviz/src/test/kotlin/org/apache/isis/client/kroviz/snapshots/sample.json
index d83647d..cb566cf 100644
--- a/incubator/clients/kroviz/src/test/kotlin/org/apache/isis/client/kroviz/snapshots/sample.json
+++ b/incubator/clients/kroviz/src/test/kotlin/org/apache/isis/client/kroviz/snapshots/sample.json
@@ -2,69 +2,114 @@
   "links": [
     {
       "rel": "self",
-      "href": "https://demo-wicket.isis.incode.work/restful/objects/demo.JavaLangStrings/PADw_eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04IiBzdGFuZGFsb25lPSJ5ZXMiPz4KPERlbW8vPgo=",
+      "href": "http://localhost:8080/restful/objects/demo.CustomUiVm/PADw_eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04IiBzdGFuZGFsb25lPSJ5ZXMiPz4KPGRlbW8uQ3VzdG9tVWlWbT4KICAgIDxhZGRyZXNzPk1hbHZlcm4sIFVLPC9hZGRyZXNzPgogICAgPGxhdGl0dWRlPjUyLjE5ODk0NDwvbGF0aXR1ZGU-CiAgICA8bG9uZ2l0dWRlPi0yLjI0MjY1NzEwNzkxMzA4ODY8L2xvbmdpdHVkZT4KICAgIDx6b29tPjE0PC96b29tPgo8L2RlbW8uQ3VzdG9tVWlWbT4K",
       "method": "GET",
       "type": "application/json;profile=\"urn:org.restfulobjects:repr-types/object\"",
-      "title": "String data type"
+      "title": "Malvern, UK"
     },
     {
       "rel": "describedby",
-      "href": "https://demo-wicket.isis.incode.work/restful/domain-types/demo.JavaLangStrings",
+      "href": "http://localhost:8080/restful/domain-types/demo.CustomUiVm",
       "method": "GET",
       "type": "application/json;profile=\"urn:org.restfulobjects:repr-types/domain-type\""
     },
     {
       "rel": "urn:org.apache.isis.restfulobjects:rels/object-layout",
-      "href": "https://demo-wicket.isis.incode.work/restful/objects/demo.JavaLangStrings/PADw_eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04IiBzdGFuZGFsb25lPSJ5ZXMiPz4KPERlbW8vPgo=/object-layout",
+      "href": "http://localhost:8080/restful/objects/demo.CustomUiVm/PADw_eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04IiBzdGFuZGFsb25lPSJ5ZXMiPz4KPGRlbW8uQ3VzdG9tVWlWbT4KICAgIDxhZGRyZXNzPk1hbHZlcm4sIFVLPC9hZGRyZXNzPgogICAgPGxhdGl0dWRlPjUyLjE5ODk0NDwvbGF0aXR1ZGU-CiAgICA8bG9uZ2l0dWRlPi0yLjI0MjY1NzEwNzkxMzA4ODY8L2xvbmdpdHVkZT4KICAgIDx6b29tPjE0PC96b29tPgo8L2RlbW8uQ3VzdG9tVWlWbT4K/object-layout",
       "method": "GET",
       "type": "application/json;profile=\"urn:org.restfulobjects:repr-types/object-layout-bs3\""
     },
     {
       "rel": "urn:org.apache.isis.restfulobjects:rels/object-icon",
-      "href": "https://demo-wicket.isis.incode.work/restful/objects/demo.JavaLangStrings/PADw_eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04IiBzdGFuZGFsb25lPSJ5ZXMiPz4KPERlbW8vPgo=/image",
+      "href": "http://localhost:8080/restful/objects/demo.CustomUiVm/PADw_eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04IiBzdGFuZGFsb25lPSJ5ZXMiPz4KPGRlbW8uQ3VzdG9tVWlWbT4KICAgIDxhZGRyZXNzPk1hbHZlcm4sIFVLPC9hZGRyZXNzPgogICAgPGxhdGl0dWRlPjUyLjE5ODk0NDwvbGF0aXR1ZGU-CiAgICA8bG9uZ2l0dWRlPi0yLjI0MjY1NzEwNzkxMzA4ODY8L2xvbmdpdHVkZT4KICAgIDx6b29tPjE0PC96b29tPgo8L2RlbW8uQ3VzdG9tVWlWbT4K/image",
       "method": "GET",
       "type": "image/png"
     },
     {
       "rel": "urn:org.restfulobjects:rels/update",
-      "href": "https://demo-wicket.isis.incode.work/restful/objects/demo.JavaLangStrings:PADw_eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04IiBzdGFuZGFsb25lPSJ5ZXMiPz4KPERlbW8vPgo=",
+      "href": "http://localhost:8080/restful/objects/demo.CustomUiVm:PADw_eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04IiBzdGFuZGFsb25lPSJ5ZXMiPz4KPGRlbW8uQ3VzdG9tVWlWbT4KICAgIDxhZGRyZXNzPk1hbHZlcm4sIFVLPC9hZGRyZXNzPgogICAgPGxhdGl0dWRlPjUyLjE5ODk0NDwvbGF0aXR1ZGU-CiAgICA8bG9uZ2l0dWRlPi0yLjI0MjY1NzEwNzkxMzA4ODY8L2xvbmdpdHVkZT4KICAgIDx6b29tPjE0PC96b29tPgo8L2RlbW8uQ3VzdG9tVWlWbT4K",
       "method": "PUT",
       "type": "application/json;profile=\"urn:org.restfulobjects:repr-types/object\"",
       "arguments": {}
     }
   ],
   "extensions": {
-    "oid": "demo.JavaLangStrings:PADw_eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04IiBzdGFuZGFsb25lPSJ5ZXMiPz4KPERlbW8vPgo=",
+    "oid": "demo.CustomUiVm:PADw_eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04IiBzdGFuZGFsb25lPSJ5ZXMiPz4KPGRlbW8uQ3VzdG9tVWlWbT4KICAgIDxhZGRyZXNzPk1hbHZlcm4sIFVLPC9hZGRyZXNzPgogICAgPGxhdGl0dWRlPjUyLjE5ODk0NDwvbGF0aXR1ZGU-CiAgICA8bG9uZ2l0dWRlPi0yLjI0MjY1NzEwNzkxMzA4ODY8L2xvbmdpdHVkZT4KICAgIDx6b29tPjE0PC96b29tPgo8L2RlbW8uQ3VzdG9tVWlWbT4K",
     "isService": false,
     "isPersistent": true
   },
-  "title": "String data type",
-  "domainType": "demo.JavaLangStrings",
-  "instanceId": "PADw_eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04IiBzdGFuZGFsb25lPSJ5ZXMiPz4KPERlbW8vPgo=",
+  "title": "Malvern, UK",
+  "domainType": "demo.CustomUiVm",
+  "instanceId": "PADw_eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04IiBzdGFuZGFsb25lPSJ5ZXMiPz4KPGRlbW8uQ3VzdG9tVWlWbT4KICAgIDxhZGRyZXNzPk1hbHZlcm4sIFVLPC9hZGRyZXNzPgogICAgPGxhdGl0dWRlPjUyLjE5ODk0NDwvbGF0aXR1ZGU-CiAgICA8bG9uZ2l0dWRlPi0yLjI0MjY1NzEwNzkxMzA4ODY8L2xvbmdpdHVkZT4KICAgIDx6b29tPjE0PC96b29tPgo8L2RlbW8uQ3VzdG9tVWlWbT4K",
   "members": {
-    "entities": {
-      "id": "entities",
-      "memberType": "collection",
+    "address": {
+      "id": "address",
+      "memberType": "property",
       "links": [
         {
-          "rel": "urn:org.restfulobjects:rels/details;collection=\"entities\"",
-          "href": "https://demo-wicket.isis.incode.work/restful/objects/demo.JavaLangStrings/PADw_eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04IiBzdGFuZGFsb25lPSJ5ZXMiPz4KPERlbW8vPgo=/collections/entities",
+          "rel": "urn:org.restfulobjects:rels/details;property=\"address\"",
+          "href": "http://localhost:8080/restful/objects/demo.CustomUiVm/PADw_eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04IiBzdGFuZGFsb25lPSJ5ZXMiPz4KPGRlbW8uQ3VzdG9tVWlWbT4KICAgIDxhZGRyZXNzPk1hbHZlcm4sIFVLPC9hZGRyZXNzPgogICAgPGxhdGl0dWRlPjUyLjE5ODk0NDwvbGF0aXR1ZGU-CiAgICA8bG9uZ2l0dWRlPi0yLjI0MjY1NzEwNzkxMzA4ODY8L2xvbmdpdHVkZT4KICAgIDx6b29tPjE0PC96b29tPgo8L2RlbW8uQ3VzdG9tVWlWbT4K/properties/address",
           "method": "GET",
-          "type": "application/json;profile=\"urn:org.restfulobjects:repr-types/object-collection\""
+          "type": "application/json;profile=\"urn:org.restfulobjects:repr-types/object-property\""
         }
-      ]
+      ],
+      "value": "Malvern, UK",
+      "extensions": {
+        "x-isis-format": "string"
+      },
+      "disabledReason": "Disabled"
     },
-    "openViewModel": {
-      "id": "openViewModel",
-      "memberType": "action",
+    "latitude": {
+      "id": "latitude",
+      "memberType": "property",
       "links": [
         {
-          "rel": "urn:org.restfulobjects:rels/details;action=\"openViewModel\"",
-          "href": "https://demo-wicket.isis.incode.work/restful/objects/demo.JavaLangStrings/PADw_eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04IiBzdGFuZGFsb25lPSJ5ZXMiPz4KPERlbW8vPgo=/actions/openViewModel",
+          "rel": "urn:org.restfulobjects:rels/details;property=\"latitude\"",
+          "href": "http://localhost:8080/restful/objects/demo.CustomUiVm/PADw_eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04IiBzdGFuZGFsb25lPSJ5ZXMiPz4KPGRlbW8uQ3VzdG9tVWlWbT4KICAgIDxhZGRyZXNzPk1hbHZlcm4sIFVLPC9hZGRyZXNzPgogICAgPGxhdGl0dWRlPjUyLjE5ODk0NDwvbGF0aXR1ZGU-CiAgICA8bG9uZ2l0dWRlPi0yLjI0MjY1NzEwNzkxMzA4ODY8L2xvbmdpdHVkZT4KICAgIDx6b29tPjE0PC96b29tPgo8L2RlbW8uQ3VzdG9tVWlWbT4K/properties/latitude",
           "method": "GET",
-          "type": "application/json;profile=\"urn:org.restfulobjects:repr-types/object-action\""
+          "type": "application/json;profile=\"urn:org.restfulobjects:repr-types/object-property\""
         }
-      ]
+      ],
+      "value": "52.198944",
+      "extensions": {
+        "x-isis-format": "string"
+      },
+      "disabledReason": "Disabled"
+    },
+    "longitude": {
+      "id": "longitude",
+      "memberType": "property",
+      "links": [
+        {
+          "rel": "urn:org.restfulobjects:rels/details;property=\"longitude\"",
+          "href": "http://localhost:8080/restful/objects/demo.CustomUiVm/PADw_eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04IiBzdGFuZGFsb25lPSJ5ZXMiPz4KPGRlbW8uQ3VzdG9tVWlWbT4KICAgIDxhZGRyZXNzPk1hbHZlcm4sIFVLPC9hZGRyZXNzPgogICAgPGxhdGl0dWRlPjUyLjE5ODk0NDwvbGF0aXR1ZGU-CiAgICA8bG9uZ2l0dWRlPi0yLjI0MjY1NzEwNzkxMzA4ODY8L2xvbmdpdHVkZT4KICAgIDx6b29tPjE0PC96b29tPgo8L2RlbW8uQ3VzdG9tVWlWbT4K/properties/longitude",
+          "method": "GET",
+          "type": "application/json;profile=\"urn:org.restfulobjects:repr-types/object-property\""
+        }
+      ],
+      "value": "-2.2426571079130886",
+      "extensions": {
+        "x-isis-format": "string"
+      },
+      "disabledReason": "Disabled"
+    },
+    "zoom": {
+      "id": "zoom",
+      "memberType": "property",
+      "links": [
+        {
+          "rel": "urn:org.restfulobjects:rels/details;property=\"zoom\"",
+          "href": "http://localhost:8080/restful/objects/demo.CustomUiVm/PADw_eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04IiBzdGFuZGFsb25lPSJ5ZXMiPz4KPGRlbW8uQ3VzdG9tVWlWbT4KICAgIDxhZGRyZXNzPk1hbHZlcm4sIFVLPC9hZGRyZXNzPgogICAgPGxhdGl0dWRlPjUyLjE5ODk0NDwvbGF0aXR1ZGU-CiAgICA8bG9uZ2l0dWRlPi0yLjI0MjY1NzEwNzkxMzA4ODY8L2xvbmdpdHVkZT4KICAgIDx6b29tPjE0PC96b29tPgo8L2RlbW8uQ3VzdG9tVWlWbT4K/properties/zoom",
+          "method": "GET",
+          "type": "application/json;profile=\"urn:org.restfulobjects:repr-types/object-property\""
+        }
+      ],
+      "value": 14,
+      "format": "int",
+      "extensions": {
+        "x-isis-format": "int"
+      },
+      "disabledReason": "Disabled"
     },
     "clearHints": {
       "id": "clearHints",
@@ -72,7 +117,7 @@
       "links": [
         {
           "rel": "urn:org.restfulobjects:rels/details;action=\"clearHints\"",
-          "href": "https://demo-wicket.isis.incode.work/restful/objects/demo.JavaLangStrings/PADw_eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04IiBzdGFuZGFsb25lPSJ5ZXMiPz4KPERlbW8vPgo=/actions/clearHints",
+          "href": "http://localhost:8080/restful/objects/demo.CustomUiVm/PADw_eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04IiBzdGFuZGFsb25lPSJ5ZXMiPz4KPGRlbW8uQ3VzdG9tVWlWbT4KICAgIDxhZGRyZXNzPk1hbHZlcm4sIFVLPC9hZGRyZXNzPgogICAgPGxhdGl0dWRlPjUyLjE5ODk0NDwvbGF0aXR1ZGU-CiAgICA8bG9uZ2l0dWRlPi0yLjI0MjY1NzEwNzkxMzA4ODY8L2xvbmdpdHVkZT4KICAgIDx6b29tPjE0PC96b29tPgo8L2RlbW8uQ3VzdG9tVWlWbT4K/actions/clearHints",
           "method": "GET",
           "type": "application/json;profile=\"urn:org.restfulobjects:repr-types/object-action\""
         }
@@ -84,7 +129,7 @@
       "links": [
         {
           "rel": "urn:org.restfulobjects:rels/details;action=\"inspectMetamodel\"",
-          "href": "https://demo-wicket.isis.incode.work/restful/objects/demo.JavaLangStrings/PADw_eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04IiBzdGFuZGFsb25lPSJ5ZXMiPz4KPERlbW8vPgo=/actions/inspectMetamodel",
+          "href": "http://localhost:8080/restful/objects/demo.CustomUiVm/PADw_eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04IiBzdGFuZGFsb25lPSJ5ZXMiPz4KPGRlbW8uQ3VzdG9tVWlWbT4KICAgIDxhZGRyZXNzPk1hbHZlcm4sIFVLPC9hZGRyZXNzPgogICAgPGxhdGl0dWRlPjUyLjE5ODk0NDwvbGF0aXR1ZGU-CiAgICA8bG9uZ2l0dWRlPi0yLjI0MjY1NzEwNzkxMzA4ODY8L2xvbmdpdHVkZT4KICAgIDx6b29tPjE0PC96b29tPgo8L2RlbW8uQ3VzdG9tVWlWbT4K/actions/inspectMetamodel",
           "method": "GET",
           "type": "application/json;profile=\"urn:org.restfulobjects:repr-types/object-action\""
         }
@@ -96,31 +141,31 @@
       "links": [
         {
           "rel": "urn:org.restfulobjects:rels/details;action=\"downloadLayoutXml\"",
-          "href": "https://demo-wicket.isis.incode.work/restful/objects/demo.JavaLangStrings/PADw_eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04IiBzdGFuZGFsb25lPSJ5ZXMiPz4KPERlbW8vPgo=/actions/downloadLayoutXml",
+          "href": "http://localhost:8080/restful/objects/demo.CustomUiVm/PADw_eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04IiBzdGFuZGFsb25lPSJ5ZXMiPz4KPGRlbW8uQ3VzdG9tVWlWbT4KICAgIDxhZGRyZXNzPk1hbHZlcm4sIFVLPC9hZGRyZXNzPgogICAgPGxhdGl0dWRlPjUyLjE5ODk0NDwvbGF0aXR1ZGU-CiAgICA8bG9uZ2l0dWRlPi0yLjI0MjY1NzEwNzkxMzA4ODY8L2xvbmdpdHVkZT4KICAgIDx6b29tPjE0PC96b29tPgo8L2RlbW8uQ3VzdG9tVWlWbT4K/actions/downloadLayoutXml",
           "method": "GET",
           "type": "application/json;profile=\"urn:org.restfulobjects:repr-types/object-action\""
         }
       ]
     },
-    "rebuildMetamodel": {
-      "id": "rebuildMetamodel",
+    "recentCommands": {
+      "id": "recentCommands",
       "memberType": "action",
       "links": [
         {
-          "rel": "urn:org.restfulobjects:rels/details;action=\"rebuildMetamodel\"",
-          "href": "https://demo-wicket.isis.incode.work/restful/objects/demo.JavaLangStrings/PADw_eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04IiBzdGFuZGFsb25lPSJ5ZXMiPz4KPERlbW8vPgo=/actions/rebuildMetamodel",
+          "rel": "urn:org.restfulobjects:rels/details;action=\"recentCommands\"",
+          "href": "http://localhost:8080/restful/objects/demo.CustomUiVm/PADw_eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04IiBzdGFuZGFsb25lPSJ5ZXMiPz4KPGRlbW8uQ3VzdG9tVWlWbT4KICAgIDxhZGRyZXNzPk1hbHZlcm4sIFVLPC9hZGRyZXNzPgogICAgPGxhdGl0dWRlPjUyLjE5ODk0NDwvbGF0aXR1ZGU-CiAgICA8bG9uZ2l0dWRlPi0yLjI0MjY1NzEwNzkxMzA4ODY8L2xvbmdpdHVkZT4KICAgIDx6b29tPjE0PC96b29tPgo8L2RlbW8uQ3VzdG9tVWlWbT4K/actions/recentCommands",
           "method": "GET",
           "type": "application/json;profile=\"urn:org.restfulobjects:repr-types/object-action\""
         }
       ]
     },
-    "recentCommands": {
-      "id": "recentCommands",
+    "rebuildMetamodel": {
+      "id": "rebuildMetamodel",
       "memberType": "action",
       "links": [
         {
-          "rel": "urn:org.restfulobjects:rels/details;action=\"recentCommands\"",
-          "href": "https://demo-wicket.isis.incode.work/restful/objects/demo.JavaLangStrings/PADw_eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04IiBzdGFuZGFsb25lPSJ5ZXMiPz4KPERlbW8vPgo=/actions/recentCommands",
+          "rel": "urn:org.restfulobjects:rels/details;action=\"rebuildMetamodel\"",
+          "href": "http://localhost:8080/restful/objects/demo.CustomUiVm/PADw_eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04IiBzdGFuZGFsb25lPSJ5ZXMiPz4KPGRlbW8uQ3VzdG9tVWlWbT4KICAgIDxhZGRyZXNzPk1hbHZlcm4sIFVLPC9hZGRyZXNzPgogICAgPGxhdGl0dWRlPjUyLjE5ODk0NDwvbGF0aXR1ZGU-CiAgICA8bG9uZ2l0dWRlPi0yLjI0MjY1NzEwNzkxMzA4ODY8L2xvbmdpdHVkZT4KICAgIDx6b29tPjE0PC96b29tPgo8L2RlbW8uQ3VzdG9tVWlWbT4K/actions/rebuildMetamodel",
           "method": "GET",
           "type": "application/json;profile=\"urn:org.restfulobjects:repr-types/object-action\""
         }
@@ -132,7 +177,7 @@
       "links": [
         {
           "rel": "urn:org.restfulobjects:rels/details;action=\"downloadMetamodelXml\"",
-          "href": "https://demo-wicket.isis.incode.work/restful/objects/demo.JavaLangStrings/PADw_eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04IiBzdGFuZGFsb25lPSJ5ZXMiPz4KPERlbW8vPgo=/actions/downloadMetamodelXml",
+          "href": "http://localhost:8080/restful/objects/demo.CustomUiVm/PADw_eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04IiBzdGFuZGFsb25lPSJ5ZXMiPz4KPGRlbW8uQ3VzdG9tVWlWbT4KICAgIDxhZGRyZXNzPk1hbHZlcm4sIFVLPC9hZGRyZXNzPgogICAgPGxhdGl0dWRlPjUyLjE5ODk0NDwvbGF0aXR1ZGU-CiAgICA8bG9uZ2l0dWRlPi0yLjI0MjY1NzEwNzkxMzA4ODY8L2xvbmdpdHVkZT4KICAgIDx6b29tPjE0PC96b29tPgo8L2RlbW8uQ3VzdG9tVWlWbT4K/actions/downloadMetamodelXml",
           "method": "GET",
           "type": "application/json;profile=\"urn:org.restfulobjects:repr-types/object-action\""
         }
@@ -144,7 +189,7 @@
       "links": [
         {
           "rel": "urn:org.restfulobjects:rels/details;action=\"openRestApi\"",
-          "href": "https://demo-wicket.isis.incode.work/restful/objects/demo.JavaLangStrings/PADw_eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04IiBzdGFuZGFsb25lPSJ5ZXMiPz4KPERlbW8vPgo=/actions/openRestApi",
+          "href": "http://localhost:8080/restful/objects/demo.CustomUiVm/PADw_eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04IiBzdGFuZGFsb25lPSJ5ZXMiPz4KPGRlbW8uQ3VzdG9tVWlWbT4KICAgIDxhZGRyZXNzPk1hbHZlcm4sIFVLPC9hZGRyZXNzPgogICAgPGxhdGl0dWRlPjUyLjE5ODk0NDwvbGF0aXR1ZGU-CiAgICA8bG9uZ2l0dWRlPi0yLjI0MjY1NzEwNzkxMzA4ODY8L2xvbmdpdHVkZT4KICAgIDx6b29tPjE0PC96b29tPgo8L2RlbW8uQ3VzdG9tVWlWbT4K/actions/openRestApi",
           "method": "GET",
           "type": "application/json;profile=\"urn:org.restfulobjects:repr-types/object-action\""
         }
diff --git a/incubator/clients/kroviz/src/test/setup.js b/incubator/clients/kroviz/src/test/setup.js
new file mode 100644
index 0000000..72ddfff
--- /dev/null
+++ b/incubator/clients/kroviz/src/test/setup.js
@@ -0,0 +1 @@
+document.body.insertAdjacentHTML('afterbegin', "<div id='root'></div>");