diff --git a/src/main/java/com/example/usecase23/UseCase23View.java b/src/main/java/com/example/usecase23/UseCase23View.java index 325b310..a7f689e 100644 --- a/src/main/java/com/example/usecase23/UseCase23View.java +++ b/src/main/java/com/example/usecase23/UseCase23View.java @@ -139,8 +139,8 @@ private Component createViewEvents() { bindData(chart, newYorkSeries, newYorkTimelineSignal); bindData(chart, tokyoSeries, tokyoTimelineSignal); - Signal.effect(chart, () -> xAxis.setCategories(timelineCategoriesSignal - .get().stream().map(Signal::get).toArray(String[]::new))); + Signal.effect(chart, () -> xAxis.setCategories( + timelineCategoriesSignal.getValues().toArray(String[]::new))); conf.addSeries(berlinSeries); conf.addSeries(londonSeries); @@ -165,12 +165,8 @@ private Component createViewEvents() { private static void bindData(Chart chart, ListSeries series, ListSignal signal) { - Signal.effect(chart, () -> { - series.setData(signal.get().stream().map(Signal::get) - .toArray(Number[]::new)); - // TODO issue of getting the values from ListSignal instead of - // Signal - }); + Signal.effect(chart, + () -> series.setData(signal.getValues().toArray(Number[]::new))); } private Component createServiceHealth() { @@ -309,7 +305,7 @@ private String getStatusDisplayName(ServiceHealth serviceHealth) { * Callback invoked by the scheduler service with new dashboard data. This * method only updates signals - no UI access or chart drawing. */ - private void onDataUpdate(DashboardData data) { + void onDataUpdate(DashboardData data) { // Update highlight card signals currentUsersSignal.set(data.currentUsers()); viewEventsSignal.set(data.viewEvents()); @@ -389,10 +385,13 @@ private HighlightCard(String title, ValueSignal signal, signal.peek().doubleValue(), signal.peek().doubleValue())); // update previous value when the main signal changes + // Uses runWithoutTransaction since we must track previous state + // No infinite loop risk: effect depends on signal, not changeSignal Signal.effect(this, () -> { double current = signal.get().doubleValue(); double previous = changeSignal.peek().current(); - changeSignal.set(new Change(previous, current)); + Signal.runWithoutTransaction( + () -> changeSignal.set(new Change(previous, current))); }); // Computed signal for percentage change @@ -415,8 +414,8 @@ private HighlightCard(String title, ValueSignal signal, valueSpan.bindText(signal.map(format::apply)); Span percentageSpan = new Span(); - percentageSpan.bindText(prefixSignal - .map(prefix -> prefix + percentageSignal.get())); + percentageSpan.bindText(Signal.computed( + () -> prefixSignal.get() + percentageSignal.get())); Icon icon = new Icon(iconSignal); icon.setSize("10px"); diff --git a/src/test/java/com/example/usecase23/UseCase23ViewTest.java b/src/test/java/com/example/usecase23/UseCase23ViewTest.java index 8d1ae51..9dc8f60 100644 --- a/src/test/java/com/example/usecase23/UseCase23ViewTest.java +++ b/src/test/java/com/example/usecase23/UseCase23ViewTest.java @@ -1,5 +1,7 @@ package com.example.usecase23; +import java.util.List; + import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.security.test.context.support.WithMockUser; @@ -9,6 +11,7 @@ import com.vaadin.flow.component.board.Board; import com.vaadin.flow.component.grid.Grid; import com.vaadin.flow.component.html.H2; +import com.vaadin.flow.component.html.Span; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -47,4 +50,55 @@ void serviceHealthGridRendered() { // Grid for service health should be present assertTrue($view(Grid.class).all().size() >= 1); } + + @Test + void highlightCardUpdatesValueOnDataUpdate() { + navigate(UseCase23View.class); + runPendingSignalsTasks(); + + UseCase23View view = $view(UseCase23View.class).first(); + + // Push data update with known values + view.onDataUpdate(createTestData(42, 1500, 3.5, 99)); + runPendingSignalsTasks(); + + // "Current users" card should show "42" + assertTrue($view(Span.class).all().stream() + .anyMatch(s -> "42".equals(s.getText())), + "Expected a Span with text '42' for current users"); + } + + @Test + void highlightCardShowsPercentageChangeAfterTwoUpdates() { + navigate(UseCase23View.class); + runPendingSignalsTasks(); + + UseCase23View view = $view(UseCase23View.class).first(); + + // First update: baseline + view.onDataUpdate(createTestData(100, 1000, 2.0, 50)); + runPendingSignalsTasks(); + + // Second update: double the users (100 → 200 = +100%) + view.onDataUpdate(createTestData(200, 1000, 2.0, 50)); + runPendingSignalsTasks(); + + // Should show "+100.0" in the percentage badge + assertTrue($view(Span.class).all().stream().anyMatch( + s -> s.getText() != null && s.getText().contains("+100.0")), + "Expected percentage badge showing +100.0"); + } + + private DashboardData createTestData(int users, int views, + double conversion, double custom) { + return new DashboardData(users, views, conversion, custom, + new DashboardData.TimelineData("12:00", 10, 20, 30, 40), + List.of(new ServiceHealth(ServiceHealth.Status.OK, + "Münster", 100, 200), + new ServiceHealth(ServiceHealth.Status.EXCELLENT, + "Cluj-Napoca", 150, 250), + new ServiceHealth(ServiceHealth.Status.FAILING, + "Ciudad Victoria", 50, 75)), + List.of(10.0, 20.0, 30.0, 15.0, 25.0, 35.0)); + } }