diff --git a/README.md b/README.md
index 82c6217450870d7fa7960d464fa9bd66b566f2f1..e59d1151b4a5773b282430899de7c981a005a7df 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,13 @@
-## Javafx template
+# Javafx template
 
-Template for single-module javafx projects, with maven setup for latest java and javafx, and jupiter and testfx for testing.
+Template for  single-module javafx project, with maven setup for Java 16 and JavaFX 16, and JUnit 5 (Jupiter) and TestFX for testing.
+
+To make the project more interesting, it is the start of an [RPN](https://en.wikipedia.org/wiki/Reverse_Polish_notation) calculator (look for `// TODO`) markers). The core logic is almost implemented (in [Calc.java](src/main/java/app/Calc.java)), the fxml file (in [App.fxml](src/main/resources/app/App.fxml) is almost complete, but the controller class (in [AppController.java](src/main/java/app/AppController.java) is pretty limited. And last, but not least, there is a TestFX-based test (in [AppTest.java](src/test/java/app/AppTest.java), see the [README](src/test/java/app/README.md) for details about what it tests.
+
+## Trying it out
+
+The project in javafx-template can be tried out in various ways:
+
+- compile with `mvn compile` (after `cd javafx-template` of course)
+- test with `mvn test` (it should fail until you complete the RPN calculator)
+- run with `mvn javafx:run` (it should open, but not work properly)
diff --git a/javafx-template/src/main/java/app/AppController.java b/javafx-template/src/main/java/app/AppController.java
index 9efef1cdd5e3f12e2ea53e51b5f631dc3d3dd966..1cbbd295aac1d9594c7610f135965215c8bbebbb 100644
--- a/javafx-template/src/main/java/app/AppController.java
+++ b/javafx-template/src/main/java/app/AppController.java
@@ -2,7 +2,6 @@ package app;
 
 import java.util.List;
 import java.util.function.BinaryOperator;
-import java.util.function.Consumer;
 import java.util.function.UnaryOperator;
 
 import javafx.event.ActionEvent;
@@ -39,16 +38,12 @@ public class AppController {
         updateOperandsView();
     }
 
-    private int minOperandCount = 2;
-
     private void updateOperandsView() {
-        while (calc.getOperandCount() < minOperandCount) {
-            calc.pushOperand(calc.getOperandCount(), 0.0);
-        }
         List<Double> operands = operandsView.getItems();
         operands.clear();
-        for (int i = 0; i < minOperandCount; i++) {
-            operands.add(calc.peekOperand(minOperandCount - i - 1));
+        int elementCount = Math.min(calc.getOperandCount(), 3);
+        for (int i = 0; i < elementCount; i++) {
+            operands.add(calc.peekOperand(elementCount - i - 1));
         }
     }
 
@@ -68,33 +63,25 @@ public class AppController {
         operandView.setText(operandString);
     }
 
-    private void setOperand(double d) {
-        setOperand(Double.toString(d));
-    }
-
-    private void clearOperand() {
-        setOperand("");
-    }    
-
     @FXML
     void handleEnter() {
         if (hasOperand()) {
             calc.pushOperand(getOperand());
         } else {
             calc.dup();
-        }    
-        clearOperand();
+        }
+        setOperand("");
         updateOperandsView();
-    }    
+    }
 
     private void appendToOperand(String s) {
-        setOperand(operandView.getText() + s);
+        // TODO
     }
 
     @FXML
     void handleDigit(ActionEvent ae) {
         if (ae.getSource() instanceof Labeled l) {
-            appendToOperand(l.getText());
+            // TODO append button label to operand
         }
     }
 
@@ -102,70 +89,40 @@ public class AppController {
     void handlePoint() {
         var operandString = getOperandString();
         if (operandString.contains(".")) {
-            setOperand(operandString.substring(0, operandString.indexOf(".") + 1));
+            // TODO remove characters after point
         } else {
-            appendToOperand(".");
+            // TODO append point
         }
     }
 
     @FXML
     void handleClear() {
-        clearOperand();
-    }
-
-    private void withOperand(Runnable proc) {
-        if (hasOperand()) {
-            calc.pushOperand(getOperand());
-            clearOperand();
-        }
-        proc.run();
-        updateOperandsView();
+        // TODO clear operand
     }
 
     private void performOperation(UnaryOperator<Double> op) {
-        withOperand(() -> calc.performOperation(op));
+        // TODO
     }
 
     private void performOperation(boolean swap, BinaryOperator<Double> op) {
-        withOperand(() -> {
-            if (swap) {
-                calc.swap();
-            }
-            calc.performOperation(op);
-        });
+        if (hasOperand()) {
+            // TODO push operand first
+        }
+        // TODO perform operation, but swap first if needed
     }
 
     @FXML
     void handleOpAdd() {
-        performOperation(false, (op1, op2) -> op1 + op2);
+        // TODO
     }
 
     @FXML
     void handleOpSub() {
-        performOperation(true, (op1, op2) -> op1 - op2);
+        // TODO
     }
 
     @FXML
     void handleOpMult() {
-        performOperation(false, (op1, op2) -> op1 * op2);
-    }
-
-    @FXML
-    void handleOpDiv() {
-        performOperation(true, (op1, op2) -> op1 / op2);
-    }
-
-    @FXML
-    void handleOpSquareRoot() {
-        performOperation(op1 -> Math.sqrt(op1));
-    }
-
-    @FXML
-    void handlePi() {
-        withOperand(() -> calc.pushOperand(Math.PI));
-    }
-
-    public static void main(String[] args) {
-        System.out.println("\u221A");
+        // TODO
     }
 }
diff --git a/javafx-template/src/main/java/app/Calc.java b/javafx-template/src/main/java/app/Calc.java
index 720035912fb68239826229c5a8968e279130f905..6102e31383fcdb96fa94197a73a7774bfcbd70a1 100644
--- a/javafx-template/src/main/java/app/Calc.java
+++ b/javafx-template/src/main/java/app/Calc.java
@@ -13,41 +13,79 @@ public class Calc {
         operandStack.addAll(List.of(operands));
     }
 
+    /**
+     * @return the number of operands on the stack
+     */
     public int getOperandCount() {
         return operandStack.size();
     }
 
+    /**
+     * Pushes a new operand into the n'th place from the top.
+     * @param n the place to push
+     * @param d the new operand
+     */
     public void pushOperand(int n, double d) {
         operandStack.add(operandStack.size() - n, d);
     }
 
+    /**
+     * Pushes a new operand onto top of the stack.
+     * @param d the new operand
+     */
     public void pushOperand(double d) {
         pushOperand(0, d);
     }
 
+    /**
+     * @param n the place (from the top) to peek
+     * @return the n'th operand from the top
+     */
     public double peekOperand(int n) {
         return operandStack.get(operandStack.size() - n - 1);
     }
 
+    /**
+     * @return the top operand
+     */
     public double peekOperand() {
         return peekOperand(0);
     }
 
+    /**
+     * Removes and returns the n'th operand from the top.
+     * @param n the place from the top to remove
+     * @return the n'th operand from the top
+     */
     public double popOperand(int n) {
         return operandStack.remove(operandStack.size() - n - 1);
     }
 
+    /**
+     * Removes and returns the top operand.
+     * @return the top operand
+     */
     public double popOperand() {
         return popOperand(0);
     }
 
+    /**
+     * Performs the provided operation in the top operand, and
+     * replaces it with the result.
+     * @param op the operation to perform
+     * @return the result of performing the operation
+     */
     public double performOperation(UnaryOperator<Double> op) {
-        var op1 = popOperand();
-        var result = op.apply(op1);
-        pushOperand(result);
-        return result;
+        // TODO
+        return 0.0;
     }
 
+    /**
+     * Performs the provided operation in the two topmost operands, and
+     * replaces them with the result.
+     * @param op the operation to perform
+     * @return the result of performing the operation
+     */
     public double performOperation(BinaryOperator<Double> op) {
         var op1 = popOperand();
         var op2 = popOperand();
@@ -56,14 +94,17 @@ public class Calc {
         return result;
     }
 
+    /**
+     * Swaps the two topmost operands.
+     */
     public void swap() {
-        var op1 = popOperand();
-        var op2 = popOperand();
-        pushOperand(op1);
-        pushOperand(op2);
+        // TODO
     }
 
+    /**
+     * Duplicates the top operand.
+     */
     public void dup() {
-        pushOperand(peekOperand());
+        // TODO
     }
 }
\ No newline at end of file
diff --git a/javafx-template/src/main/resources/app/App.fxml b/javafx-template/src/main/resources/app/App.fxml
index 4669ea86207440f682edfc75ab91eb722cce8e97..41f4e4f1816de5e5e8134f3f6c45307279f7e554 100644
--- a/javafx-template/src/main/resources/app/App.fxml
+++ b/javafx-template/src/main/resources/app/App.fxml
@@ -13,6 +13,7 @@
    <Label text="" fx:id="operandView"
       GridPane.rowIndex="1" GridPane.columnIndex="0" GridPane.columnSpan="4"/>
 
+   <!-- multi-line button label with XML entity for newline -->
    <Button text="E&#10;n&#10;t&#10;e&#10;r" onAction="#handleEnter"
       GridPane.rowIndex="2" GridPane.columnIndex="3" GridPane.rowSpan="4"/>
 
@@ -47,10 +48,12 @@
       GridPane.rowIndex="6" GridPane.columnIndex="1"/>
    <Button text="*" onAction="#handleOpMult"
       GridPane.rowIndex="6" GridPane.columnIndex="2"/>
-   <Button text="/" onAction="#handleOpDiv"
+
+   <!-- TODO -->
+   <Button text="/"
       GridPane.rowIndex="6" GridPane.columnIndex="3"/>
-   <Button text="√" onAction="#handleOpSquareRoot"   
+   <Button text="√"
       GridPane.rowIndex="7" GridPane.columnIndex="0"/>
-   <Button text="π" onAction="#handlePi"   
+   <Button text="π"
       GridPane.rowIndex="7" GridPane.columnIndex="1"/>
 </GridPane>
diff --git a/javafx-template/src/test/java/app/AppTest.java b/javafx-template/src/test/java/app/AppTest.java
index aa6dc6e0b0673311745876e8a799ad8f534f4265..209ebfc63ace75f7894281ca73ec606f61da4798 100644
--- a/javafx-template/src/test/java/app/AppTest.java
+++ b/javafx-template/src/test/java/app/AppTest.java
@@ -9,7 +9,6 @@ import javafx.stage.Stage;
 
 import java.io.IOException;
 import java.util.List;
-import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
 import org.junit.jupiter.api.Assertions;
@@ -17,7 +16,6 @@ import org.junit.jupiter.api.Test;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.Arguments;
 import org.junit.jupiter.params.provider.MethodSource;
-import org.opentest4j.AssertionFailedError;
 import org.testfx.framework.junit5.ApplicationTest;
 import org.testfx.matcher.control.LabeledMatchers;
 
@@ -106,7 +104,7 @@ public class AppTest extends ApplicationTest {
         for (var label : labels.split(" ")) {
             click(label.equals("\n") ? enterLabel : label);
         }
-        checkView(Stream.of(operandsString.split(" ")).mapToDouble(Double::valueOf).toArray());
+        checkView("", Stream.of(operandsString.split(" ")).mapToDouble(Double::valueOf).toArray());
     }
 
     private static Stream<Arguments> testClicksOperands() {
diff --git a/javafx-template/src/test/java/app/README.md b/javafx-template/src/test/java/app/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..0cc59802a04720e2dfddaa5c14c029328b91ef33
--- /dev/null
+++ b/javafx-template/src/test/java/app/README.md
@@ -0,0 +1,38 @@
+# Tests for the RPN calculator
+
+This folder/package contains tests based on TestFX for the RPN Calculator (currently only one test class).
+
+As can be seen when launching, the app contains a list (top) showing the operands
+(topmost operand at the bottom), a text field (below list, initially empty) for a new operand and
+the buttons for digits, enter, decimal point, operations etc.
+
+## What is tested
+
+The tests simulate clicks on the buttons and checks that the underlying Calc object,
+the list (a view of the Calc object's operand stack) and the text field are updated as expected.
+E.g. if you click buttons `2 3 . 5` the string `23.5` should be shown,
+while the list is not affected. If you then click `enter`, the text field should be emptied, the operand stack should have `23.5` at the top and the list should have `23.5` at the bottom 
+(logically the top of the operand stack).
+
+Below are the specific cases that are tested.
+
+buttons to click `=>` text field content:
+
+- `2 7` => `27`
+- `2 7 .` => `27.`
+- `2 7 . 5` => `27.5`
+- `2 7 . 5 .` => `27.` (cut at decimal point)
+
+buttons to click `=>` operand stack/list content (from the bottom):
+
+- `2 7 . 5 enter"` => `27.5`
+- `2 7 enter` => `27.0"`
+- `2 enter 7 enter 5 enter` => `5.0 7.0 2.0` 
+- `2 7 . enter` => `27.0` 
+- `2 7 . 5 enter` => `27.5` 
+- `2 enter 7 +` => `9.0` 
+- `2 enter 7 -` => `-5.0` 
+- `2 enter 7 *` => `14.0` 
+- `6 enter 3 /` => `2.0` 
+- `2 5 enter √` => `5.0` 
+- `π` => `3.1415...` (the value of the `Math.PI` constant)