Tristan971/EasyFXML

View on GitHub
easyfxml-junit/src/main/java/moe/tristan/easyfxml/junit/SpringBootComponentTest.java

Summary

Maintainability
A
0 mins
Test Coverage
/*
 * Copyright 2017 - 2019 EasyFXML project and contributors
 *
 *    Licensed 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 moe.tristan.easyfxml.junit;

import static java.util.Collections.emptyList;
import static org.awaitility.Awaitility.await;

import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;

import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.testfx.framework.junit5.ApplicationExtension;
import org.testfx.framework.junit5.ApplicationTest;
import org.testfx.service.query.PointQuery;

import javafx.application.Platform;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

@SpringBootTest
@ExtendWith(ApplicationExtension.class)
public abstract class SpringBootComponentTest extends ApplicationTest {

    protected final FxNodeAsyncQuery withNodes(Node... nodes) {
        return FxNodeAsyncQuery.withNodes(nodes);
    }

    protected Supplier<Boolean> showing(Node node) {
        return () -> {
            final PointQuery pointQuery = point(node);
            return pointQuery.query() != null;
        };
    }

    public static final class FxNodeAsyncQuery {

        private final List<Node> nodes;

        private List<Supplier<Boolean>> nodesReady = emptyList();

        private List<Runnable> actions = emptyList();

        private List<Supplier<Boolean>> witnesses = emptyList();

        FxNodeAsyncQuery(Node[] nodes) {
            this.nodes = List.of(nodes);
        }

        public static FxNodeAsyncQuery withNodes(Node... nodes) {
            return new FxNodeAsyncQuery(nodes);
        }

        @SafeVarargs
        public final FxNodeAsyncQuery startWhen(Supplier<Boolean>... readyCheck) {
            this.nodesReady = List.of(readyCheck);
            return this;
        }

        public final FxNodeAsyncQuery willDo(Runnable... actionsWhenReady) {
            this.actions = List.of(actionsWhenReady);
            return this;
        }

        @SafeVarargs
        public final void andAwaitFor(Supplier<Boolean>... awaited) {
            this.witnesses = List.of(awaited);
            run();
        }

        public void run() {
            runTestQuery(nodes, nodesReady, actions, witnesses);
        }

        private void runTestQuery(List<Node> nodes, List<Supplier<Boolean>> nodesReady, List<Runnable> actions, List<Supplier<Boolean>> witnesses) {
            CompletableFuture
                .runAsync(() -> buildStageWithNodes(nodes), Platform::runLater)
                .thenRunAsync(() -> await().until(() -> allEvaluateToTrue(nodesReady)))
                .thenRunAsync(() -> actions.forEach(Runnable::run)).join();

            for (Supplier<Boolean> witness : witnesses) {
                await().until(witness::get);
            }
        }

        private static void buildStageWithNodes(List<Node> nodes) {
            Stage testStage = new Stage();
            testStage.setScene(new Scene(new VBox(nodes.toArray(new Node[0]))));
            testStage.show();
            testStage.toFront();
        }

        private static Boolean allEvaluateToTrue(List<Supplier<Boolean>> nodesReady) {
            return nodesReady.stream().map(Supplier::get).reduce((prev, cur) -> prev && cur).orElse(true);
        }

    }

}