+ * Example: + *
{@code public class ManualTestExample extends ManualTestWindow { + * public ManualTestExample() { + * super( + * "Manual Test Example", + * """ + * Instructions: + * 1. you will see a button named "Test" + * 2. press the button + * 3. verify that the button can be pressed""", + * 400, 250 + * ); + * } + * + * public static void main(String[] args) throws Exception { + * launch(args); + * } + * + * @Override + * protected Node createContent() { + * return new Button("Test"); + * } + * } + * }+ */ +public abstract class ManualTestWindow extends Application { + /** + * This method creates the {@code Node} containing elements under test, + * to be shown below the instructions and above the "Pass"/"Fail" buttons. + * @return the node + */ + protected abstract Node createContent(); + + private final String title; + private final String instructions; + private double width = 1000; + private double height = 800; + + public ManualTestWindow(String title, String instructions) { + this.title = title; + this.instructions = instructions; + } + + public ManualTestWindow(String title, String instructions, double width, double height) { + this(title, instructions); + this.width = width; + this.height = height; + } + + private Parent createContent(Stage stage) { + Node content = createContent(); + + BlurType blurType = BlurType.GAUSSIAN; + Color color = Color.gray(0, 0.5); + double radius = 10; + double spread = 0; + double offsetX = 1; + double offsetY = 1; + DropShadow shadow = new DropShadow(blurType, color, radius, spread, offsetX, offsetY); + + BorderPane cp = new BorderPane(content); + cp.setMargin(content, new Insets(10)); + cp.setBackground(Background.fill(Color.gray(1))); + cp.setEffect(shadow); + + Node instructionField = toTextFlow(instructions); + + Region fill = new Region(); + + Button failButton = new Button("Fail"); + setIcon(failButton, "✘", Color.RED); + failButton.setMinWidth(100); + failButton.setOnAction((ev) -> { + Platform.exit(); + throw new AssertionError("Failed Manual Test: " + stage.getTitle()); + }); + + Button passButton = new Button("Pass"); + setIcon(passButton, "✔", Color.GREEN); + passButton.setMinWidth(100); + passButton.setOnAction((ev) -> { + Platform.exit(); + }); + + HBox buttons = new HBox( + 10, + fill, + failButton, + passButton + ); + HBox.setHgrow(fill, Priority.ALWAYS); + + VBox vb = new VBox( + 10, + instructionField, + cp, + buttons + ); + vb.setPadding(new Insets(10)); + VBox.setVgrow(cp, Priority.ALWAYS); + return vb; + } + + /** + * Prepares the Application primary stage: creates the content {@code Node} to be tested, + * creates the manual test UI, sets the {@code Scene}. + * This method is called before the primary stage is shown. + * @param stage the primary stage + */ + protected void prepareStage(Stage stage) { + Parent content = createContent(stage); + stage.setWidth(width); + stage.setHeight(height); + stage.setTitle(title); + stage.setScene(new Scene(content)); + } + + @Override + public void start(Stage stage) throws Exception { + prepareStage(stage); + stage.show(); + } + + private static Node toTextFlow(String text) { + TextFlow f = new TextFlow(); + Text t = new Text(text); + f.getChildren().add(t); + f.setOnContextMenuRequested((ev) -> { + ContextMenu m = new ContextMenu(); + MenuItem mi = new MenuItem("Copy Instructions"); + mi.setOnAction((e) -> { + ClipboardContent cc = new ClipboardContent(); + cc.putString(text); + Clipboard.getSystemClipboard().setContent(cc); + }); + m.getItems().setAll(mi); + m.show(f, ev.getScreenX(), ev.getScreenY()); + }); + return f; + } + + private static void setIcon(Button b, String text, Color c) { + Text t = new Text(text); + t.setFill(c); + b.setGraphic(t); + } +}