Discover custom ViewModels and validate Computed bindings in shared hosts

This commit is contained in:
2026-11-13 13:27:23 +01:00
parent 5a208d2336
commit 99d93a11fc
46 changed files with 1397 additions and 64 deletions
@@ -41,6 +41,10 @@ public final class PreviewChecks {
public static void main(String[] args) throws Exception {
String mode = args[0];
if (mode.equals("viewmodels")) {
ViewModelRuntimeChecks.run();
return;
}
if (mode.equals("unstable")
|| mode.equals("full")
|| mode.equals("full-empty")
@@ -129,6 +133,51 @@ public final class PreviewChecks {
System.getProperty("fixture.converter.initialized") == null,
"Discovery initialized Converter");
}
check(
metadata.viewModel().equals("fixture.components.PersonViewModel"),
"Explicit custom ViewModel");
check(
metadata.attributes().stream().anyMatch(a -> a.path().equals("badge")),
"Custom property absent");
check(
metadata.computed().stream().anyMatch(m -> m.method().equals("#greeting")),
"Computed method absent");
for (String resource : java.util.List.of("Replacement", "BadgeCard")) {
String path = "fixture/components/" + resource + ".fxml";
try (var in = loader.getResourceAsStream(path)) {
var result =
BindingDiscovery.inspect(
new String(in.readAllBytes(), java.nio.charset.StandardCharsets.UTF_8),
"badge",
"view",
"badge",
"text",
path,
loader);
check(result.problems().isEmpty(), "Custom resource rejected: " + result.problems());
check(
result
.controller()
.equals(
"fixture.components."
+ (resource.equals("BadgeCard") ? "BadgeCard" : "ReplacementController")),
"Wrong resource Controller");
check(
result.attributes().stream().anyMatch(a -> a.path().equals("badge")),
"Missing custom resource property");
if (resource.equals("Replacement"))
check(
result.attributes().stream().noneMatch(a -> a.path().equals("name")),
"Replacement inherited Model");
}
}
for (String key :
java.util.List.of(
"fixture.viewmodel.initialized",
"fixture.controller.initialized",
"fixture.replacement.initialized",
"fixture.badgecard.initialized"))
check(System.getProperty(key) == null, "Discovery initialized " + key);
System.out.println("PASS bindings " + checks);
return;
}
@@ -0,0 +1,59 @@
package fixture.checks;
import de.neitzel.fx.view.*;
import de.neitzel.injection.ApplicationContext;
import fixture.model.Person;
import java.util.concurrent.*;
import javafx.application.Platform;
import javafx.scene.control.*;
public final class ViewModelRuntimeChecks {
public static void run() throws Exception {
var ready = new CountDownLatch(1);
Platform.startup(
() -> {
Platform.setImplicitExit(false);
ready.countDown();
});
if (!ready.await(10, TimeUnit.SECONDS)) throw new AssertionError("Toolkit startup");
try {
PreviewChecks.fx(
() -> {
var loader = ViewModelRuntimeChecks.class.getClassLoader();
for (String name : java.util.List.of("Custom", "Replacement")) {
var model = new Person();
var context = new ApplicationContext("fixture.components");
var view =
ViewLoader.load(
loader.getResource("fixture/components/" + name + ".fxml"), model, context);
var field = (TextField) view.getRoot().lookup("#badge");
PreviewChecks.check(
field
.getText()
.equals(name.equals("Custom") ? "Custom badge" : "Replacement badge"),
"Custom property initial value");
field.setText("Edited badge");
PreviewChecks.check(
view.property("badge").getValue().equals("Edited badge"),
"Custom property writeback");
PreviewChecks.check(
!((Label) view.getRoot().lookup("#computed")).getText().isEmpty(),
"Computed method did not run");
if (name.equals("Custom")) {
PreviewChecks.check(
view.property("name").getValue().equals(model.getName()),
"Subclass accessor shadowed Model attribute");
PreviewChecks.check(
view.property("latestMessage") != null, "Missing reserved property");
} else
PreviewChecks.check(
view.property("name") == null, "Replacement inherited Model attribute");
}
return null;
});
System.out.println("PASS viewmodels " + PreviewChecks.checks);
} finally {
Platform.exit();
}
}
}