Giter Club home page Giter Club logo

testfx's Introduction

TestFX 4

TestFX 4 CI Maven Central Chat on Gitter

Simple and clean testing for JavaFX.

TestFX 4 requires Java version of 8 (1.8), or higher, and has only legacy support.

Documentation

  • See the Javadocs for latest master.
  • See the changelog CHANGES.md for latest released version.

Features

  • A fluent and clean API.
  • Flexible setup and cleanup of JavaFX test fixtures.
  • Simple robots to simulate user interactions.
  • Rich collection of matchers and assertions to verify expected states of JavaFX scene-graph nodes.

Support for:

Gradle

To add a dependency on TestFX using Gradle, use the following:

dependencies {
    testCompile "org.testfx:testfx-core:4.0.18"
}

Java 11+

Beginning with Java 11, JavaFX is no longer part of the JDK. It has been extracted to its own project: OpenJFX. This means, extra dependencies must be added to your project.

The easiest way to add the JavaFX libraries to your Gradle project is to use the JavaFX Gradle Plugin.

After following the README for the JavaFX Gradle Plugin you will end up with something like:

plugins {
    id 'org.openjfx.javafxplugin' version '0.0.8'
}

javafx {
    version = '12'
    modules = [ 'javafx.controls', 'javafx.fxml' ]
}

Test Framework

Next add a dependency corresponding to the testing framework you are using in your project. TestFX currently supports JUnit 4, JUnit 5, and Spock.

JUnit 4

dependencies {
    testCompile "junit:junit:4.13-beta-3"
    testCompile "org.testfx:testfx-junit:4.0.18"
}

JUnit 5

dependencies {
    testCompile 'org.junit.jupiter:junit-jupiter-api:5.5.1'
    testCompile "org.testfx:testfx-junit5:4.0.18"
}

Spock

dependencies {
    testCompile "org.spockframework:spock-core:1.3-groovy-2.5"
    testCompile "org.testfx:testfx-spock:4.0.18"
}

Matcher/Assertions Library

Finally you must add a dependency corresponding to the matcher/assertions libraries that you want to use with TestFX. TestFX currently supports Hamcrest matchers or AssertJ assertions.

Hamcrest

testCompile group: 'org.hamcrest', name: 'hamcrest', version: '2.1'

AssertJ

testCompile group: 'org.assertj', name: 'assertj-core', version: '3.13.2'

Maven

To add a dependency on TestFX using Maven, use the following:

<dependency>
    <groupId>org.testfx</groupId>
    <artifactId>testfx-core</artifactId>
    <version>4.0.18</version>
    <scope>test</scope>
</dependency>

Java 11+

Beginning with Java 11, JavaFX is no longer part of the JDK. It has been extracted to its own project: OpenJFX. This means, extra dependencies must be added to your project.

The easiest way to add the JavaFX libraries to your Maven project is to use the JavaFX Maven Plugin.

After following the README for the JavaFX Maven Plugin you will end up with something like:

<dependencies>
    <dependency>
        <groupId>org.openjfx</groupId>
        <artifactId>javafx-controls</artifactId>
        <version>12.0.2</version>
    </dependency>
</dependencies>

<plugins>
    <plugin>
        <groupId>org.openjfx</groupId>
        <artifactId>javafx-maven-plugin</artifactId>
        <version>0.0.3</version>
        <configuration>
            <mainClass>hellofx/org.openjfx.App</mainClass>
        </configuration>
    </plugin>
</plugins>

Have a look at Maven Central's org.openjfx entry for an overview of available modules.

Test Framework

Next add a dependency corresponding to the testing framework you are using in your project. TestFX currently supports JUnit 4, JUnit 5, and Spock.

JUnit 4

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13-beta-3</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.testfx</groupId>
    <artifactId>testfx-junit</artifactId>
    <version>4.0.18</version>
    <scope>test</scope>
</dependency>

JUnit 5

<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-api</artifactId>
    <version>5.5.1</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.testfx</groupId>
    <artifactId>testfx-junit5</artifactId>
    <version>4.0.18</version>
    <scope>test</scope>
</dependency>

Spock

<dependency>
    <groupId>org.spockframework</groupId>
    <artifactId>spock-core</artifactId>
    <version>1.3-groovy-2.5</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.testfx</groupId>
    <artifactId>testfx-spock</artifactId>
    <version>4.0.18</version>
    <scope>test</scope>
</dependency>

Matcher/Assertions Library

Finally you must add a dependency corresponding to the matcher/assertions libraries that you want to use with TestFX. TestFX currently supports Hamcrest matchers or AssertJ assertions.

Hamcrest

<dependency>
    <groupId>org.hamcrest</groupId>
    <artifactId>hamcrest</artifactId>
    <version>2.1</version>
    <scope>test</scope>
</dependency>

AssertJ

<dependency>
    <groupId>org.assertj</groupId>
    <artifactId>assertj-core</artifactId>
    <version>3.13.2</version>
    <scope>test</scope>
</dependency>

Examples

Hamcrest Matchers

TestFX brings along a couple of custom Hamcrest matchers in package org.testfx.matcher.*.

AssertJ based Assertions

TestFX uses its own AssertJ based assertion implementation class: org.testfx.assertions.api.Assertions.

JUnit 4 with Hamcrest Matchers

import org.junit.Test;
import org.testfx.api.FxAssert;
import org.testfx.framework.junit.ApplicationTest;
import org.testfx.matcher.control.LabeledMatchers;

import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class ClickableButtonTest_JUnit4Hamcrest extends ApplicationTest {

    private Button button;

    /**
     * Will be called with {@code @Before} semantics, i. e. before each test method.
     */
    @Override
    public void start(Stage stage) {
        button = new Button("click me!");
        button.setOnAction(actionEvent -> button.setText("clicked!"));
        stage.setScene(new Scene(new StackPane(button), 100, 100));
        stage.show();
    }

    @Test
    public void should_contain_button_with_text() {
        FxAssert.verifyThat(".button", LabeledMatchers.hasText("click me!"));
    }

    @Test
    public void when_button_is_clicked_text_changes() {
        // when:
        clickOn(".button");

        // then:
        FxAssert.verifyThat(".button", LabeledMatchers.hasText("clicked!"));
    }
}

JUnit 4 with AssertJ based Assertions

import org.junit.Test;
import org.testfx.assertions.api.Assertions;
import org.testfx.framework.junit.ApplicationTest;

import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class ClickableButtonTest_JUnit4AssertJ extends ApplicationTest {

    private Button button;

    /**
     * Will be called with {@code @Before} semantics, i. e. before each test method.
     */
    @Override
    public void start(Stage stage) {
        button = new Button("click me!");
        button.setOnAction(actionEvent -> button.setText("clicked!"));
        stage.setScene(new Scene(new StackPane(button), 100, 100));
        stage.show();
    }

    @Test
    public void should_contain_button_with_text() {
        Assertions.assertThat(button).hasText("click me!");
    }

    @Test
    public void when_button_is_clicked_text_changes() {
        // when:
        clickOn(".button");

        // then:
        Assertions.assertThat(button).hasText("clicked!");
    }
}

JUnit 5

TestFX uses JUnit5's new extension mechanism via org.junit.jupiter.api.extension.ExtendWith. By using this, implementors are not forced anymore to inherit from ApplicationTest and are free to choose their own super classes.

It does also make use of JUnit5's new dependency injection mechanism. By using this, test methods have access to the FxRobot instance that must be used in order to execute actions within the UI.

JUnit 5 with Hamcrest Matchers
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.testfx.api.FxAssert;
import org.testfx.api.FxRobot;
import org.testfx.framework.junit5.ApplicationExtension;
import org.testfx.framework.junit5.Start;
import org.testfx.matcher.control.LabeledMatchers;

import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

@ExtendWith(ApplicationExtension.class)
class ClickableButtonTest_JUnit5Hamcrest {

    private Button button;

    /**
     * Will be called with {@code @Before} semantics, i. e. before each test method.
     *
     * @param stage - Will be injected by the test runner.
     */
    @Start
    private void start(Stage stage) {
        button = new Button("click me!");
        button.setId("myButton");
        button.setOnAction(actionEvent -> button.setText("clicked!"));
        stage.setScene(new Scene(new StackPane(button), 100, 100));
        stage.show();
    }

    /**
     * @param robot - Will be injected by the test runner.
     */
    @Test
    void should_contain_button_with_text(FxRobot robot) {
        FxAssert.verifyThat(button, LabeledMatchers.hasText("click me!"));
        // or (lookup by css id):
        FxAssert.verifyThat("#myButton", LabeledMatchers.hasText("click me!"));
        // or (lookup by css class):
        FxAssert.verifyThat(".button", LabeledMatchers.hasText("click me!"));
    }

    /**
     * @param robot - Will be injected by the test runner.
     */
    @Test
    void when_button_is_clicked_text_changes(FxRobot robot) {
        // when:
        robot.clickOn(".button");

        // then:
        FxAssert.verifyThat(button, LabeledMatchers.hasText("clicked!"));
        // or (lookup by css id):
        FxAssert.verifyThat("#myButton", LabeledMatchers.hasText("clicked!"));
        // or (lookup by css class):
        FxAssert.verifyThat(".button", LabeledMatchers.hasText("clicked!"));
    }
}

JUnit 5 with AssertJ Assertions

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.testfx.api.FxRobot;
import org.testfx.assertions.api.Assertions;
import org.testfx.framework.junit5.ApplicationExtension;
import org.testfx.framework.junit5.Start;

import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

@ExtendWith(ApplicationExtension.class)
class ClickableButtonTest_JUnit5AssertJ {

    private Button button;

    /**
     * Will be called with {@code @Before} semantics, i. e. before each test method.
     *
     * @param stage - Will be injected by the test runner.
     */
    @Start
    private void start(Stage stage) {
        button = new Button("click me!");
        button.setId("myButton");
        button.setOnAction(actionEvent -> button.setText("clicked!"));
        stage.setScene(new Scene(new StackPane(button), 100, 100));
        stage.show();
    }

    /**
     * @param robot - Will be injected by the test runner.
     */
    @Test
    void should_contain_button_with_text(FxRobot robot) {
        Assertions.assertThat(button).hasText("click me!");
        // or (lookup by css id):
        Assertions.assertThat(robot.lookup("#myButton").queryAs(Button.class)).hasText("click me!");
        // or (lookup by css class):
        Assertions.assertThat(robot.lookup(".button").queryAs(Button.class)).hasText("click me!");
        // or (query specific type):
        Assertions.assertThat(robot.lookup(".button").queryButton()).hasText("click me!");
    }

    /**
     * @param robot - Will be injected by the test runner.
     */
    @Test
    void when_button_is_clicked_text_changes(FxRobot robot) {
        // when:
        robot.clickOn(".button");

        // then:
        Assertions.assertThat(button).hasText("clicked!");
        // or (lookup by css id):
        Assertions.assertThat(robot.lookup("#myButton").queryAs(Button.class)).hasText("clicked!");
        // or (lookup by css class):
        Assertions.assertThat(robot.lookup(".button").queryAs(Button.class)).hasText("clicked!");
        // or (query specific type)
        Assertions.assertThat(robot.lookup(".button").queryButton()).hasText("clicked!");
    }
}

Spock with Hamcrest Matchers

import org.testfx.framework.spock.ApplicationSpec;

class ClickableButtonSpec extends ApplicationSpec {
    @Override
    void init() throws Exception {
        FxToolkit.registerStage { new Stage() }
    }

    @Override
    void start(Stage stage) {
        Button button = new Button('click me!')
        button.setOnAction { button.setText('clicked!') }
        stage.setScene(new Scene(new StackPane(button), 100, 100))
        stage.show()
    }

    @Override
    void stop() throws Exception {
        FxToolkit.hideStage()
    }

    def "should contain button"() {
        expect:
        verifyThat('.button', hasText('click me!'))
    }

    def "should click on button"() {
        when:
        clickOn(".button")

        then:
        verifyThat('.button', hasText('clicked!'))
    }
}

Continuous Integration (CI)

Travis CI

To run TestFX tests as part of your Travis CI build on Ubuntu and/or macOS take the following steps:

  1. Ensure that your unit tests are triggered as part of your build script. This is usually the default case when using Maven or Gradle.

  2. If you wish to test in a headless environment your must add Monocle as a test dependency:

    build.gradle

    dependencies {
        testCompile "org.testfx:openjfx-monocle:8u76-b04" // jdk-9+181 for Java 9, jdk-11+26 for Java 11
    }

    pom.xml

    <dependency>
        <groupId>org.testfx</groupId>
        <artifactId>openjfx-monocle</artifactId>
        <version>8u76-b04</version> <!-- jdk-9+181 for Java 9, jdk-11+26 for Java 11 -->
        <scope>test</scope>
    </dependency>
  3. Base your Travis configuration on the following. Some different build variations are shown (Glass/AWT robot, Headed/Headless, (Hi)DPI, etc.) adjust the build matrix to your requirements.

    .travis.yml

    language: java
    
    sudo: false   # Linux OS: run in container
    
    matrix:
      include:
        # Ubuntu Linux (trusty) / Oracle JDK 8 / Headed (AWT Robot)
        - os: linux
          dist: trusty
          jdk: oraclejdk8
          env:
            - _JAVA_OPTIONS="-Dtestfx.robot=awt"
        # Ubuntu Linux (trusty) / Oracle JDK 8 / Headed (Glass Robot) / HiDPI
        - os: linux
          dist: trusty
          jdk: oraclejdk8
          env:
            - _JAVA_OPTIONS="-Dtestfx.robot=glass -Dglass.gtk.uiScale=2.0"
        # Ubuntu Linux (trusty) / Oracle JDK 8 / Headless
        - os: linux
          dist: trusty
          jdk: oraclejdk8
          env:
            - _JAVA_OPTIONS="-Djava.awt.headless=true -Dtestfx.robot=glass -Dtestfx.headless=true -Dprism.order=sw"
        # macOS / Oracle JDK 8 / Headless
        - os: osx
          osx_image: xcode9.4
          jdk: oraclejdk8
          env:
            - _JAVA_OPTIONS="-Djava.awt.headless=true -Dtestfx.robot=glass -Dtestfx.headless=true -Dprism.order=sw -Dprism.verbose=true"
        # Headed macOS is not currently possible on Travis.
    
    addons:
      apt:
        packages:
          - oracle-java8-installer
    
    before_install:
      - if [[ "${TRAVIS_OS_NAME}" == linux ]]; then export DISPLAY=:99.0; sh -e /etc/init.d/xvfb start; fi
    
    install: true
    
    before_script:
      - if [[ "${TRAVIS_OS_NAME}" == osx ]]; then brew update; brew cask reinstall caskroom/versions/java8; fi
    
    script:
      - ./gradlew check
    
    before_cache:
      - rm -f  $HOME/.gradle/caches/modules-2/modules-2.lock
      - rm -fr $HOME/.gradle/caches/*/plugin-resolution/
      - rm -f  $HOME/.gradle/caches/*/fileHashes/fileHashes.bin
      - rm -f  $HOME/.gradle/caches/*/fileHashes/fileHashes.lock
    
    cache:
      directories:
        - $HOME/.gradle/caches/
        - $HOME/.gradle/wrapper/
        - $HOME/.m2

Your TestFX tests should now run as part of your Travis CI build.

Appveyor (Windows)

To run TestFX tests as part of your Appveyor build on Windows take the following steps:

  1. Ensure that your unit tests are triggered as part of your build script. This is usually the default case when using Maven or Gradle.

  2. If you wish to test in a headless environment your must add Monocle as a test dependency:

    build.gradle

    dependencies {
        testCompile "org.testfx:openjfx-monocle:8u76-b04" // jdk-9+181 for Java 9
    }

    pom.xml

    <dependency>
        <groupId>org.testfx</groupId>
        <artifactId>openjfx-monocle</artifactId>
        <version>8u76-b04</version> <!-- jdk-9+181 for Java 9 -->
        <scope>test</scope>
    </dependency>
  3. Base your Appveyor configuration on the following. Some different build variations are shown (Glass/AWT robot, Headed/Headless, (Hi)DPI, etc.) adjust the build matrix to your requirements.

    appveyor.yml

    version: "{branch} {build}"
    environment:
      matrix:
        # Java 8 / AWT Robot
        - JAVA_VERSION: "8"
          JAVA_HOME: C:\Program Files\Java\jdk1.8.0
          _JAVA_OPTIONS: "-Dtestfx.robot=awt -Dtestfx.awt.scale=true"
        # Java 8 / AWT Robot / HiDPI
        - JAVA_VERSION: "8"
          JAVA_HOME: C:\Program Files\Java\jdk1.8.0
          _JAVA_OPTIONS: "-Dtestfx.robot=awt -Dtestfx.awt.scale=true -Dglass.win.uiScale=200%"
        # Java 8 / Headless
        - JAVA_VERSION: "8"
          JAVA_HOME: C:\Program Files\Java\jdk1.8.0
          _JAVA_OPTIONS: "-Djava.awt.headless=true -Dtestfx.robot=glass -Dtestfx.headless=true -Dprism.order=sw -Dprism.text=t2k"
        # Java 10 / AWT Robot / HiDPI
        - JAVA_VERSION: "10"
          JAVA_HOME: C:\jdk10
          _JAVA_OPTIONS: "-Dtestfx.robot=awt -Dtestfx.awt.scale=true -Dglass.win.uiScale=200%"
        # Java 11 / AWT Robot / HiDPI
        - JAVA_VERSION: "11"
          JAVA_HOME: C:\jdk11
          _JAVA_OPTIONS: "-Dtestfx.robot=awt -Dtestfx.awt.scale=true -Dglass.win.uiScale=200%"
    
    build_script:
      - ps: |
          if ($env:JAVA_VERSION -eq "11") {
            $client = New-Object net.webclient
            $client.DownloadFile('http://jdk.java.net/11/', 'C:\Users\appveyor\openjdk11.html')
            $openJdk11 = cat C:\Users\appveyor\openjdk11.html | where { $_ -match "href.*https://download.java.net.*jdk11.*windows-x64.*zip\`"" } | %{ $_ -replace "^.*https:", "https:" } | %{ $_ -replace ".zip\`".*$", ".zip" }
            echo "Download boot JDK from: $openJdk11"
            $client.DownloadFile($openJdk11, 'C:\Users\appveyor\openjdk11.zip')
            Expand-Archive -Path 'C:\Users\appveyor\openjdk11.zip' -DestinationPath 'C:\Users\appveyor\openjdk11'
            Copy-Item -Path 'C:\Users\appveyor\openjdk11\*\' -Destination 'C:\jdk11' -Recurse -Force
          }
          elseif ($env:JAVA_VERSION -eq "10") {
            choco install jdk10 --version 10.0.2 --force --cache 'C:\ProgramData\chocolatey\cache' -params 'installdir=c:\\jdk10'
          }
    
          // Note: Currently Java 8 is the default JDK, if that changes the above will have to change accordingly.
    
    shallow_clone: true
    
    build:
      verbosity: detailed
    
    test_script:
      - gradlew build --no-daemon
    
    cache:
      - C:\Users\appveyor\.gradle\caches
      - C:\Users\appveyor\.gradle\wrapper -> .gradle-wrapper\gradle-wrapper.properties
      - C:\ProgramData\chocolatey\bin -> appveyor.yml
      - C:\ProgramData\chocolatey\lib -> appveyor.yml
      - C:\ProgramData\chocolatey\cache -> appveyor.yml

Chat

Head over to our gitter chat for discussion and questions.

TestFX Legacy: Deprecated

The testfx-legacy subproject is deprecated and no longer supported. It is highly recommended that you switch from using testfx-legacy. If you want to continue using it you should cap the versions of testfx-core and testfx-legacy to 4.0.8-alpha, which was the last released version of testfx-legacy. Using a newer version of testfx-core with an older version of testfx-legacy will very likely break (and does with testfx-core versions past 4.0.10-alpha).

Credits

Thanks to all of the contributors of TestFX!

testfx's People

Contributors

aalmiray avatar arnobl avatar brcolow avatar hastebrot avatar htmoia avatar janmosigitemis avatar jcornaz avatar jlleitschuh avatar joachimprinzbach avatar johnzeringue avatar jordanmartinez avatar jotomo avatar jtkb avatar lgringo avatar marcono1234 avatar minisu avatar mvsoder avatar naimdjon avatar oliver-loeffler avatar ortner avatar philipp91 avatar ralphsteinhagen avatar raniejade avatar rladstaetter avatar scolej avatar seggcsont avatar sergei-ivanov avatar simy4 avatar svenruppert avatar waffle-iron avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

testfx's Issues

Unit-Test exception under linux

Sometimes my own UI tests freeze on linux, but I'm pretty sure that's because of the PopUp-Menu bug in JavaFX 2.2 (not TestFX-related). To double-check, I ran the TestFX tests and ran into this exception:


Test set: com.eviware.loadui.ui.fx.util.test.FXScreenControllerTest

Tests run: 3, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 4.273 sec <<< FAILURE!
com.eviware.loadui.ui.fx.util.test.FXScreenControllerTest Time elapsed: 1.611 sec <<< ERROR!
java.lang.IllegalStateException: Not on FX application thread; currentThread = main
at com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:237)
at com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:400)
at javafx.stage.Window.setShowing(Window.java:778)
at javafx.stage.Window.hide(Window.java:804)
at javafx.stage.Stage.close(Stage.java:1007)
at com.eviware.loadui.ui.fx.util.test.FXScreenControllerTest.closeWindow(FXScreenControllerTest.java:88)

I don't know if the exception is relevant, it doesn't bother me. However, it is reproducible (occurs every time).

Freeze during Drag-Drop

When I do something like drag("A").to("B"), the UI test freezes.

  • The UI itself does not freeze, the OS never reports a not-responding application.
  • The testing suddenly continues when I move the mouse a little bit (manually) and the drag-drop process completes successfully.
  • When I abort the drag-drop process by pressing Escape, switching to another window, clicking, etc. the testing continues as well, however the drag-drop failed (of course) and so does the test - but it continues and terminates.
  • If I have subsequent drag-drop operations in my test, each of them freezes and needs to be unfrozen manually.
  • No freezing occurs if I drag something that doesn't have a drag-action defined with setOnDragDetected. Also if I have an onDrag-Listener, but never call event.startDragAndDrop() in there, the test doesn't freeze.
  • Before the test freezes, the mouse moves a little bit - probably the bit that it takes to raise the onDrag event. The freezing occurs very quickly, so that the mouse cannot leave the node that it is about to drag, if it is too large.

Unfortunately I haven't managed to debug in particular the TextFX code being executed after the onDrag event occured, because whenever I move the mouse or switch windows in order to pause the application in Eclipse, the test continues ...
My guess is that there are two threads waiting for an event (or for each other).

TableViews.row() breaks after some time

The JavaFX TableViewSkin implementation (at least in Java8) makes use of the possibility to "reuse" row and cell objects, which causes to appear (on the UI) in a different order than they have in the scene graph. I haven't prepared an example to reproduce this, but it should be pretty clear, that the current implementation of TableViews.row() cannot work (I was too quick on that :-)). In my own application, the problem occurs after only two or three content changes: Everything is in order, except the first row is at the very end in the scene graph.

The same problem might occur in ListViews.

Java 8: Exception "Not on FX application thread" in TextInputControlsTest

Ran all tests from IntelliJ IDEA 13 on Windows 7 (x64). Throws this exception with Java build 1.8.0-ea-b120 (x64), but runs without exception with Java build 1.7.0_45-b18 (x64).

java.lang.IllegalStateException: Not on FX application thread; currentThread = main
    at com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:210)
    at com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:393)
    at javafx.scene.Scene.addToDirtyList(Scene.java:521)
    at javafx.scene.Node.addToSceneDirtyList(Node.java:417)
    at javafx.scene.Node.impl_markDirty(Node.java:408)
    at javafx.scene.shape.Shape.impl_markDirty(Shape.java:930)
    at javafx.scene.Node.impl_geomChanged(Node.java:3758)
    at javafx.scene.text.Text.impl_geomChanged(Text.java:772)
    at javafx.scene.text.Text.needsTextLayout(Text.java:202)
    at javafx.scene.text.Text.needsFullTextLayout(Text.java:197)
    at javafx.scene.text.Text.access$400(Text.java:96)
    at javafx.scene.text.Text$4.invalidated(Text.java:396)
    at javafx.beans.property.StringPropertyBase.markInvalid(StringPropertyBase.java:109)
    at javafx.beans.property.StringPropertyBase.access$000(StringPropertyBase.java:49)
    at javafx.beans.property.StringPropertyBase$Listener.invalidated(StringPropertyBase.java:229)
    at com.sun.javafx.binding.ExpressionHelper$SingleInvalidation.fireValueChangedEvent(ExpressionHelper.java:135)
    at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:80)
    at javafx.beans.binding.StringBinding.invalidate(StringBinding.java:171)
    at com.sun.javafx.binding.BindingHelperObserver.invalidated(BindingHelperObserver.java:51)
    at com.sun.javafx.binding.ExpressionHelper$Generic.fireValueChangedEvent(ExpressionHelper.java:339)
    at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:80)
    at javafx.scene.control.TextInputControl$TextProperty.fireValueChangedEvent(TextInputControl.java:1116)
    at javafx.scene.control.TextInputControl$TextProperty.markInvalid(TextInputControl.java:1120)
    at javafx.scene.control.TextInputControl$TextProperty.invalidate(TextInputControl.java:1060)
    at javafx.scene.control.TextInputControl$TextProperty.access$200(TextInputControl.java:1032)
    at javafx.scene.control.TextInputControl$1.invalidated(TextInputControl.java:130)
    at com.sun.javafx.binding.ExpressionHelper$SingleInvalidation.fireValueChangedEvent(ExpressionHelper.java:135)
    at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:80)
    at javafx.scene.control.TextField$TextFieldContent.delete(TextField.java:91)
    at javafx.scene.control.TextInputControl$TextProperty.doSet(TextInputControl.java:1128)
    at javafx.scene.control.TextInputControl$TextProperty.set(TextInputControl.java:1055)
    at javafx.scene.control.TextInputControl.setText(TextInputControl.java:279)
    at javafx.scene.control.TextInputControl.clear(TextInputControl.java:925)
    at org.loadui.testfx.controls.TextInputControls.clearTextIn(TextInputControls.java:24)
    at org.loadui.testfx.TextInputControlsTest.shouldClearText(TextInputControlsTest.java:32)

Functionality to clear TextFields

And similar controls. Using push(KeyCode.BACK_SPACE) in a loop is too ugly for such a common action.

Please let me know if you start working on this so we don't do double-work.

Stateful system testing?

In LoadUI (the project from which I extracted TestFX), we have two types of tests that we use TestFX for: GUI unit tests and System tests.

The class required to do stateful system testing, TestState, is currently not a part of TestFX, but could easily be migrated. The question is if this would be useful to anyone.

It works like this: You have a tree of TestStates, the first one being for example ApplicaitonStarted. All states knows how to enterFromParent and exitToParent. A system test must implement TestState getStartingState() instead of getRootNode(). The system tests can be executed either by starting from scratch for every test, or in a continous flow (do all the tests without ever shutting down the application under test). You can see a video of the system tests being executed here.

Please share your thoughts on this.

Freeze if last click terminates application

If you do something like click("Close-Button") and the close button closes the stage and the stage was the last one in the application, the JavaFX application will terminate and the UI loop will quit. However, click has an internal awaitEvents() that waits for the UI loop, causing the unit-test thread to run forever.

Issue with Double Click on Secondary Mouse Button

GuiTest.doubleClick(MouseButton.SECONDARY)

... leads to a first click on the secondary mouse button and the second click on primary mouse button. We resolved this in out projects by using

click(MouseButton.SECONDARY).click(MouseButton.SECONDARY).sleep(50);

instead.

visual comparision (screenshot)

Sometimes it may be convenient to test if the visual representation is correct. TestFX already knows how to make screenshots, so it would be ideal if it could assert that a certain node looks as a predefined image.

Initially this may seem like an easy fix, but this may pose a challenge; I'm uncertain if nodes on different systems / resolutions really look exactly the same. It's very likely things like gradients have deviations. So comparing could become, ah, interesting. Maybe an "almost identical" approach would be better, e.g a 99% match.
assertSimilar( loadImage("abc.png"), makeScreenshot(node), 0.99)

Or things like edge detection can be used, filtering out cosmetics like gradients. This probably would work on controls, but less so on elaborate graphics like Gerrit Grunwald's original gauges.
assertSimilar( loadImage("abc.png"), makeScreenshot(node), 0.99, Filters.edgeDetection())

TestFX with Groovy and Spock

Hi!

I'm a big fan of Groovy and Spock and become more and more a fan of JavaFX and TestFX. 😃

It turns out that tests in TestFX need to inherit from org.loadui.testfx.GuiTest and Spock specs need to inherit from spock.lang.Specification. So composition comes into play. The gist https://gist.github.com/hastebrot/8375513 contains a simple Spock spec with TestFX.

A feature description looks like this:

def "enters gate with right password"() {              
    when:                                              
    fx.click("#password").type("fidelio")              
    fx.click("#submit")                                

    then:                                              
    verifyThat("#message", hasText("please enter!"))   
}                                                      

The stage setup method setupScene uses a Groovy closure, but it might change later into an abstract Parent getRootNode method.

def setup() {
    setupStage {
        return new Label(id: "message", text: "hello world")
    }
}

I just started to migrate my FEST Swing tests to TestFX and I'm very amazed. Well done!

P.S.: Looking forward to contribute some pull requests. Spock integration, more flexible access to test Stages and Scenes, and more flexible screenshots are my first ideas.

extended GuiTest

In order to add some convenience methods, I've extended GuiTest with an abstract JFXtrasGuiTest. I needed extending, because click, etc is only available there.

However, the framework tries to run that class, and complains first about no runnable methods and then about getRootNode not being implemented.
Suggestions:

  1. do not fail, but only warn and then skip, if a class does not contain runnable methods,
  2. check if a class is abstract and skip it

TestFX crashes test thread

When I run the basic test example, the test thread crashes.

The error differs from how I run the test.
command line mvn test on the module:

[xcb] Unknown request in queue while dequeuing
[xcb] Most likely this is a multi-threaded client and XInitThreads has not been called
[xcb] Aborting, sorry about that.
java: ../../src/xcb_io.c:179: dequeue_pending_request: Assertion `!xcb_xlib_unknown_req_in_deq' failed.
Aborted (core dumped)

In NetBeans right click test on the test class:

*** glibc detected *** /usr/lib/jvm/java-7-oracle/jre/bin/java: free(): invalid next size (normal): 0x00007f5e78041b70 ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x7eb96)[0x7f5ed6f9ab96]
/lib/x86_64-linux-gnu/libglib-2.0.so.0(+0x37313)[0x7f5eb7451313]
/lib/x86_64-linux-gnu/libglib-2.0.so.0(+0x375eb)[0x7f5eb74515eb]
/lib/x86_64-linux-gnu/libglib-2.0.so.0(+0x3798a)[0x7f5eb745198a]
/usr/lib/x86_64-linux-gnu/libgdk-x11-2.0.so.0(+0x64fd6)[0x7f5ebd38efd6]
/usr/lib/x86_64-linux-gnu/libgdk-x11-2.0.so.0(gdk_x11_get_xatom_by_name_for_display+0x75)[0x7f5ebd38f485]
/usr/lib/x86_64-linux-gnu/libgdk-x11-2.0.so.0(+0x5923f)[0x7f5ebd38323f]
/usr/lib/x86_64-linux-gnu/libgdk-x11-2.0.so.0(gdk_x11_screen_supports_net_wm_hint+0x9e)[0x7f5ebd38628e]
/usr/lib/x86_64-linux-gnu/libgdk-x11-2.0.so.0(gdk_window_focus+0x100)[0x7f5ebd3976c0]
/usr/lib/jvm/java-7-oracle/jre/lib/amd64/libglass.so(Java_com_sun_glass_ui_gtk_GtkWindow__1requestFocus+0xd)[0x7f5ec41f0a8d]
[0x7f5ecd012738]

JDK:
java version "1.7.0_40"
Java(TM) SE Runtime Environment (build 1.7.0_40-b43)
Java HotSpot(TM) 64-Bit Server VM (build 24.0-b56, mixed mode)

Maven:
Apache Maven 3.0.3 (r1075438; 2011-02-28 18:31:09+0100)
Maven home: /usr/share/maven3
Java version: 1.7.0_40, vendor: Oracle Corporation
Java home: /usr/lib/jvm/java-7-oracle/jre
Default locale: en_US, platform encoding: UTF-8
OS name: "linux", version: "3.2.0-49-generic", arch: "amd64", family: "unix"

pressRelease() method?

I have the following problem:

When I use press(KeyCode.someKey).release(KeyCode.someKey) and the action triggered by this key takes a while, the UI-test thread will be blocked in press()->FXScreenController.press()->FXTestUtils.awaitEvents() until the action completed. If this is >.5 seconds, the keypress will be repeated (because the releasing happens too late), so the action is triggered twice. In my case, this even leads hundreds of keypresses and the whole application either freezes or crashes.

A solution might be a pressRelease() method, that only waits once (after completing everything).

Such a method might be useful anyway, for example if used like this:
pressRelease(KeyCode.CONTROL, KeyCode.SHIFT, KeyCode.X);
This line might be interpreted and executed as:
press(CONTROL).press(SHIFT).press(X).release(X).release(SHIFT).release(CONTROL);

Note how the release order is inversed, making it easier to execute complex key combinations.

I assigned myself to this issue, just tell me what you think about it. At least the method name "pressRelease" should be reconsidered, maybe even the whole idea?

TableView support

"Clicking a row/cell & asserting its value would be great. My feature wish list: double/right-click row, select 1+ rows, edit a cell"

Provide full example for a TestFX test

Hi,

I've been looking around for some proper test suites for JavaFX and after looking into JemmyFX I stumbled upon TestFX. Unfortunately, even after looking into the some examples I was not able to figure out how to test my application (without mocking everything).

All I want to do is: Perform some moves on my JavaFX-Application (which is being implemented in the Main.class of the project) - just like with Selenium - is that even possible with TestFX or am I just missing something here? What do I have to implement in the getRootNode() in order to have a link to my real application?

The Login-Form example does only provide a screenshot and only a few lines of code - which does not really help. All other examples kind of mock everything.

What I would like to see is the whole project (javafx app & test => src & test files) of that project to download or at least more information to see what's going on - without any mockups/mocked code but the real application.

That would be really great - I'm kind of stuck right now.

Regards,

mastix

Documentation severely out of date

It seems that much of the (sparse) documentation in the README is no longer correct -- the Maven repository does not, as far as I can tell, contain the artifact, and there is no more "GuiTest" class to extend. It is entirely unclear how to use this at all.

Unit-Tests don't run serially in Eclipse

You might want to close this as wontfix, it is a minor Eclipse-related (-caused) issue:

Running multiple TestFX-based unit tests in Eclipse doesn't work because Eclipse runs all tests in the same JVM and Application.launch() protects itself against being launched more than once.

Fix bad equals()

MouseInfo.getPointerInfo().getLocation().equals( point )

// this is aPoint.equals( aPoint2D )
// although Point extends Point2D, so this will always be false.
// flip the order of the equals to make it work.

findByCssSelector throws IllegalArgumentException

If two elements exist (globally) that match the same CSS selector (e.g. two windows each containing a #someId) and one of them is hidden, findByCssSelector throws an IllegalArgumentException (stack trace below).

The reason is:
Set locallyFound = findAll( selector );
Iterables.addAll(locallyFound, globallyFound);

findAll() uses getVisibleNodes(), which uses com.google.common.collect.Sets.filter(Set, Predicate).
This filter() method does not return a set that has been filled with the filtered items, but a FilteredSet based on the given Set. In particular, the FilteredSet (ending up in "locallyFound") will not allow an element to be added, which does not match its filter.

java.lang.IllegalArgumentException
at com.google.common.base.Preconditions.checkArgument(Preconditions.java:76)
at com.google.common.collect.Collections2$FilteredCollection.add(Collections2.java:126)
at com.google.common.collect.Iterators.addAll(Iterators.java:418)
at com.google.common.collect.Iterables.addAll(Iterables.java:334)
at org.loadui.testfx.GuiTest.findByCssSelector(GuiTest.java:443)
at org.loadui.testfx.GuiTest.find(GuiTest.java:300)
at org.loadui.testfx.GuiTest.pointFor(GuiTest.java:1159)
at org.loadui.testfx.GuiTest.move(GuiTest.java:849)
[...]

Quick fix 1:
Change getVisibleNodes() or findAll() to return a HashSet (or some other set) that copies the elements from the FilteredSet.

Quick fix 2:
Change findByCssSelector() to first filter globallyFound() before adding them to locallyFound.

add assertions method

Perhaps another way to do it in Java would be to create a custom JUnit testrunner, and do like this:

@test
public void doTheGuiInteractions()
{
...
}

@Assert
public void assertThings_willBeRunInJavaFxThread()
{
...
}
[10:43:59] Renato: I removed my blog post so they can publish it on SmartBear's blog. When they give me a link to the blog I will just add it to my blog so links to it will not be broken.
[10:44:10] Henrik Olsson: Although the proxy solution would be limited. It would only be able to create proxies for Interfaces if we implement it the same way Mockito does.
[10:44:23] Renato: ah, that's true.
[10:45:33] Renato: Or is it?
[10:45:44] Renato: I thought they found a way around that.
[10:46:01] Henrik Olsson: The second alternative would also need to link @test and @Assert, e.g:

@test("Test that foo is bar")
@Assert("Test that foo is bar")

test do not start through

I was able to build TestFX, include it in the JFXtras project and I've copied the essence of the example test over and created one of my own. The test starts but halts at the

stage = stageFuture.get( 25, TimeUnit.SECONDS );

line, where it will throw a TimeoutException

java.util.concurrent.TimeoutException: Timeout waiting for task.
at com.google.common.util.concurrent.AbstractFuture$Sync.get(AbstractFuture.java:269)
at com.google.common.util.concurrent.AbstractFuture.get(AbstractFuture.java:96)
at jfxtras.labs.scene.control.test.ListSpinnerTest2.createWindow(ListSpinnerTest2.java:69)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:27)
at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

CDI integration

Previous discussion (from other issue):

@svenruppert:

Hi, as Henrik wrote.. I am working on an CDI bootstrap for TestFX. The first tests are good. So I would like to create three maven modules. The first one would be the parent with the dep management. the second the TestFX without CDI and the third one based on the second one with CDI support.
For the combination I would use an aproch like I described in my blog: http://www.rapidpm.org/2014/01/fxcontroller-with-cdi-managed.html. Same for the CDI bootstrap. let me know if this would be ok for you all. If yes, I would create the three modules first, so that we can merge this soon to master branch.

@Philipp91:

@svenruppert Do you have something like a per-window Context/Scope for your CDI integration?
I have my own CDI bootstrap for JavaFX. Integrating this with TestFX was as easy as replacing the internally used javafx.application.Application-implementation by my own one. That's why I asked about this feature above. Maybe you could use that, too?
I don't understand what the second maven module (TestFX without CDI) is about. Wouldn't it be enough to have TestFX, CDI4FX (or whatever your CDI-JavaFX integration is called) - both of which already exist - and only one more module that contains a class like CDIGuiTest, which extends GuiTest?

@svenruppert:

@Philipp91 Well the CDI module would have more deps.. additional classes on cdi side and changes inside the non-CDI module, because I would bootstrap the tests itself too. But in a way you don´t need Arquillian. The FX stuff is mostly Java SE and if you don´t want to use CDI you don´t want to have this deps/classes inside your project.
I would say, three modules would be the clean way to do it..
If possible I would like to see your CDI bootstrap, and I think to integrate a additionaly scope would be possible.

@hastebrot:

@svenruppert I really like your concept with the three maven modules.

@Philipp91:

Okay, so your TestFX integration allows to use injection inside the test classes? That sounds great. I currently have to use methods like "getBean(SomeClass.class)" etc. ...

You can see my implementation, it is currently integrated with a project and therefore in aprivate BitBucket repository. I can add you there or send you an email. However, I wouldn't consider it stable. It needs to intercept all (JavaFX) events, which does not work in all cases. Mostly PopupWindows (used by Menu, ChoiceBox, etc.) cause problems. It works fine for this specific application, but currently, we have to use special "AttachedMenuItem"s etc. instead of the normal JavaFX classes.
Refer to https://community.oracle.com/thread/2592504 for details and to https://javafx-jira.kenai.com/browse/RT-33543 for a possible solution (scheduled for Java 9 ...). But maybe there is a different, better way to do this.
The general problem that needs to be solved is that the same thread (JavaFX main UI thread) is responsible for all windows, so at some place someone needs to tell which window the thread is currently working on.

@svenruppert:

@Philipp91 Maybe I have a basic step for a solution.. ContextResolver are usefull in my projects .. but this for later.. And yes, you can use CDI inside the tests (and the TestFX Framework itself) too.. and no Arquillian needed.. The first tests are all green.

What are the final steps?
First finishing the refactorings and tests..
After this creating the three modules ( I could do it if it is ok)
After this I would branch again for the CDI support.

add interceptors using cglib

assertThat( ".node" ).getCategory().equals("Runners");

assertThat would in this case return a proxy that intercepts any method calls and wraps them in Platform.runLater().

We would do this because of this:
"""
It is always dangerous to directly access objects within Java FX
hierarchy. All the access should be done through event queue. If done
otherwise results are unpredictable at the best.
"""

Pause-Button

Another idea: Pretty often I would have loved to have a button like the "Pause"-button on the keyboard to press in order to pause the UI tests. This would be useful to cancel the tests (because you can't if the mouse moves all the time) and to interrupt them (because they run >10 minutes) in order to do someting like answer an e-mail, etc.

I don't know how this could be implemented. Maybe at the beginning of every TestFX method (or maybe only the important ones) call a method that does the following:

  • Check if the pause key is pressed (somehow).
  • If not, continue normally.
  • If it is pressed, determine and save the currently active window.
  • Show a modal dialog that says "The UI tests have been paused. Click to continue."
  • Reactivate the previously active window.

So maybe you would have to press that button for a little while until the next call of the check method is reached, but that would be fine. Waiting 2-3 seconds is way better than either waiting 5 minutes or canceling and waiting the whole 10 minutes again later.

add support for qualifiers

My controls have behavior depending on qualifiers (shift, alt, control); for example in CalendarPicker when LMB clicking with shift pressed, the range from the previous last selected date is added to the selection. Methods like click could have a qualifier second parameter, for example;

click("#buttonX", SHIFT + CONTROL)

Combine Label-matching with CSS selectors

Very often, I need to click something that is ambiguous. I have stopped using CSS classes long ago, because something like .button is obviously not specific enough. Nesting them doesn't help much, either.
IDs are also a little difficult, because you would have to give each and every node an ID and ignore a warning for each ID that doesn't have a matching field on the controller. Also, IDs tend to be either lengthy (like "closeButtonInSettingsTab") or ambiguous ("closeButton").
That's mostly why I use labels for clicking stuff. Another advantage is that the tests become more readable.

But also with labels, I have the problem that they can be ambiguous. Just think of the "File" menu when two windows are open. This can of course be solved by something like click(find("Target label", find("#moreSpecificLocation")). Due to some odd behavior of Java 8, I can't use click(find(...)), but have to write click((Node) find(...)) instead. All that code gets pretty lengthy and unreadable.

Is there any good solution to this already? I want to do something like:

  • Click a node labeled "Teachers", but I want the tab to be clicked and not the button and not the label, which happen to have the same label.
  • Click a node that appears n times (e.g. once per table row or list row).
  • Click a node in a different window.

One idea could be to extend CSS selectors. As far as I can see, there is nothing like type selectors in JavaFX (unlike body in web CSS). So every selectors starts with a . or a #. Hence, we could interpret selectors that do not start with one of these characters as labels and allow something like this:

  • click("Teachers.tab")
  • click("#myTabPane > Teachers")
  • click(".button-area > Teachers")

Also we could allow pseudo-classes that do not actually exist in JavaFX (just like jQuery interprets :checked and :visible) and allow something like:

  • #someTable :row(14) :cell(2)
  • #someNode:parent
  • :focused
  • ...

Downsides:

  • Probably hard to implement.
  • Might be confusing to users.
  • Might be error-prone.

Window activation

The tests sometimes fail on my system because TestFX does not automatically activate the window anymore (at least not as often as it did with 2.x). In particular when I run the tests from Maven instead of Eclipse, the console window tends to stay in the foreground, but only sometimes, and only during the first UI-test-class. Therefore, at most one test will fail and sometimes everything works.
The mouse moves to the correct position (my consoles are transparent, so I can confirm that everything is calculated correctly), but the click ends up in the console window and the test fails.

On failed test, TestFX tries to save a screenshot in my C:\ directory

Error:

java.lang.RuntimeException: Query #runners.category resulted in no nodes found! Screenshot saved as C:\screenshot.png at com.eviware.loadui.ui.fx.util.test.TestFX.find(TestFX.java:175) at com.eviware.loadui.ui.fx.util.test.TestFX.pointFor(TestFX.java:608) at com.eviware.loadui.ui.fx.util.test.TestFX.move(TestFX.java:359) at com.eviware.loadui.ui.fx.util.test.LoadUiRobot.scrollToCategoryOf(LoadUiRobot.java:124) at com.eviware.loadui.ui.fx.util.test.LoadUiRobot.expandCategoryOf(LoadUiRobot.java:111) at com.eviware.loadui.ui.fx.util.test.LoadUiRobot.createComponentAt(LoadUiRobot.java:66) at com.eviware.loadui.ui.fx.util.test.LoadUiRobot.createComponent(LoadUiRobot.java:57) at com.eviware.loadui.test.ui.fx.FxIntegrationBase.create(FxIntegrationBase.java:33) at com.eviware.loadui.test.ui.fx.tablelog.TableLogTest.should_haveNoRows_whenCreated(TableLogTest.java:34) -

(RandomAccessFile.java:233) at javax.imageio.stream.FileImageOutputStream.(FileImageOutputStream.java:69) at com.sun.imageio.spi.FileImageOutputStreamSpi.createOutputStreamInstance(FileImageOutputStreamSpi.java:55) at javax.imageio.ImageIO.createImageOutputStream(ImageIO.java:419) at javax.imageio.ImageIO.write(ImageIO.java:1530) at com.eviware.loadui.ui.fx.util.test.TestFX.captureScreenshot(TestFX.java:188) at com.eviware.loadui.ui.fx.util.test.TestFX.find(TestFX.java:175) at com.eviware.loadui.ui.fx.util.test.TestFX.pointFor(TestFX.java:608) at com.eviware.loadui.ui.fx.util.test.TestFX.move(TestFX.java:359) at com.eviware.loadui.ui.fx.util.test.LoadUiRobot.scrollToCategoryOf(LoadUiRobot.java:124) at com.eviware.loadui.ui.fx.util.test.LoadUiRobot.expandCategoryOf(LoadUiRobot.java:111) at com.eviware.loadui.ui.fx.util.test.LoadUiRobot.createComponentAt(LoadUiRobot.java:66) at com.eviware.loadui.ui.fx.util.test.LoadUiRobot.createComponent(LoadUiRobot.java:57) at com.eviware.loadui.test.ui.fx.FxIntegrationBase.create(FxIntegrationBase.java:33) at com.eviware.loadui.test.ui.fx.tablelog.TableLogTest.should_haveNoRows_whenCreated(TableLogTest.java:34) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:601) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20) at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28) at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:30) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222) at org.junit.runners.ParentRunner.run(ParentRunner.java:300) at org.junit.runners.Suite.runChild(Suite.java:128) at org.junit.runners.Suite.runChild(Suite.java:24) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222) at org.junit.runners.ParentRunner.run(ParentRunner.java:300) at org.junit.runner.JUnitCore.run(JUnitCore.java:157) at org.junit.runner.JUnitCore.run(JUnitCore.java:136) at org.apache.maven.surefire.junitcore.JUnitCoreWrapper.createReqestAndRun(JUnitCoreWrapper.java:139) at org.apache.maven.surefire.junitcore.JUnitCoreWrapper.executeEager(JUnitCoreWrapper.java:111) at org.apache.maven.surefire.junitcore.JUnitCoreWrapper.execute(JUnitCoreWrapper.java:84) at org.apache.maven.surefire.junitcore.JUnitCoreProvider.invoke(JUnitCoreProvider.java:138) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:601) at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray2(ReflectionUtils.java:208) at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:159) at org.apache.maven.surefire.booter.ProviderFactory.invokeProvider(ProviderFactory.java:87) at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:153) at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:95) java.lang.NullPointerException at javax.imageio.ImageIO.write(ImageIO.java:1538) at com.eviware.loadui.ui.fx.util.test.TestFX.captureScreenshot(TestFX.java:188) at com.eviware.loadui.ui.fx.util.test.TestFX.find(TestFX.java:175) at com.eviware.loadui.ui.fx.util.test.TestFX.pointFor(TestFX.java:608) at com.eviware.loadui.ui.fx.util.test.TestFX.move(TestFX.java:359) at com.eviware.loadui.ui.fx.util.test.LoadUiRobot.scrollToCategoryOf(LoadUiRobot.java:124) at com.eviware.loadui.ui.fx.util.test.LoadUiRobot.expandCategoryOf(LoadUiRobot.java:111) at com.eviware.loadui.ui.fx.util.test.LoadUiRobot.createComponentAt(LoadUiRobot.java:66) at com.eviware.loadui.ui.fx.util.test.LoadUiRobot.createComponent(LoadUiRobot.java:57) at com.eviware.loadui.test.ui.fx.FxIntegrationBase.create(FxIntegrationBase.java:33) at com.eviware.loadui.test.ui.fx.tablelog.TableLogTest.should_haveNoRows_whenCreated(TableLogTest.java:34) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:601) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20) at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28) at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:30) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222) at org.junit.runners.ParentRunner.run(ParentRunner.java:300) at org.junit.runners.Suite.runChild(Suite.java:128) at org.junit.runners.Suite.runChild(Suite.java:24) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222) at org.junit.runners.ParentRunner.run(ParentRunner.java:300) at org.junit.runner.JUnitCore.run(JUnitCore.java:157) at org.junit.runner.JUnitCore.run(JUnitCore.java:136) at org.apache.maven.surefire.junitcore.JUnitCoreWrapper.createReqestAndRun(JUnitCoreWrapper.java:139) at org.apache.maven.surefire.junitcore.JUnitCoreWrapper.executeEager(JUnitCoreWrapper.java:111) at org.apache.maven.surefire.junitcore.JUnitCoreWrapper.execute(JUnitCoreWrapper.java:84) at org.apache.maven.surefire.junitcore.JUnitCoreProvider.invoke(JUnitCoreProvider.java:138) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:601) at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray2(ReflectionUtils.java:208) at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:159) at org.apache.maven.surefire.booter.ProviderFactory.invokeProvider(ProviderFactory.java:87) at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:153) at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:95) java.io.FileNotFoundException: \screenshot.png (Access is denied) at java.io.RandomAccessFile.open(Native Method) at java.io.RandomAccessFile.(RandomAccessFile.java:233) at javax.imageio.stream.FileImageOutputStream.(FileImageOutputStream.java:69) at com.sun.imageio.spi.FileImageOutputStreamSpi.createOutputStreamInstance(FileImageOutputStreamSpi.java:55) at javax.imageio.ImageIO.createImageOutputStream(ImageIO.java:419) at javax.imageio.ImageIO.write(ImageIO.java:1530) at com.eviware.loadui.ui.fx.util.test.TestFX.captureScreenshot(TestFX.java:188) at com.eviware.loadui.ui.fx.util.test.TestFX.find(TestFX.java:175) at com.eviware.loadui.ui.fx.util.test.TestFX.pointFor(TestFX.java:608) at com.eviware.loadui.ui.fx.util.test.TestFX.move(TestFX.java:359) at com.eviware.loadui.ui.fx.util.test.TestFX.click(TestFX.java:320) at com.eviware.loadui.test.ui.fx.states.ProjectLoadedWithoutAgentsState.exitToParent(ProjectLoadedWithoutAgentsState.java:76) at com.eviware.loadui.test.TestState.transitionTo(TestState.java:121) at com.eviware.loadui.test.TestState.enter(TestState.java:99) at com.eviware.loadui.test.ui.fx.FxIntegrationTestBase.teardown(FxIntegrationTestBase.java:20) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:601) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42) at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:36) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222) at org.junit.runners.ParentRunner.run(ParentRunner.java:300) at org.junit.runners.Suite.runChild(Suite.java:128) at org.junit.runners.Suite.runChild(Suite.java:24) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222) at org.junit.runners.ParentRunner.run(ParentRunner.java:300) at org.junit.runner.JUnitCore.run(JUnitCore.java:157) at org.junit.runner.JUnitCore.run(JUnitCore.java:136) at org.apache.maven.surefire.junitcore.JUnitCoreWrapper.createReqestAndRun(JUnitCoreWrapper.java:139) at org.apache.maven.surefire.junitcore.JUnitCoreWrapper.executeEager(JUnitCoreWrapper.java:111) at org.apache.maven.surefire.junitcore.JUnitCoreWrapper.execute(JUnitCoreWrapper.java:84) at org.apache.maven.surefire.junitcore.JUnitCoreProvider.invoke(JUnitCoreProvider.java:138) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:601) at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray2(ReflectionUtils.java:208) at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:159) at org.apache.maven.surefire.booter.ProviderFactory.invokeProvider(ProviderFactory.java:87) at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:153) at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:95) java.lang.NullPointerException at javax.imageio.ImageIO.write(ImageIO.java:1538) at com.eviware.loadui.ui.fx.util.test.TestFX.captureScreenshot(TestFX.java:188) at com.eviware.loadui.ui.fx.util.test.TestFX.find(TestFX.java:175) at com.eviware.loadui.ui.fx.util.test.TestFX.pointFor(TestFX.java:608) at com.eviware.loadui.ui.fx.util.test.TestFX.move(TestFX.java:359) at com.eviware.loadui.ui.fx.util.test.TestFX.click(TestFX.java:320) at com.eviware.loadui.test.ui.fx.states.ProjectLoadedWithoutAgentsState.exitToParent(ProjectLoadedWithoutAgentsState.java:76) at com.eviware.loadui.test.TestState.transitionTo(TestState.java:121) at com.eviware.loadui.test.TestState.enter(TestState.java:99) at com.eviware.loadui.test.ui.fx.FxIntegrationTestBase.teardown(FxIntegrationTestBase.java:20) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:601) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42) at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:36) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222) at org.junit.runners.ParentRunner.run(ParentRunner.java:300) at org.junit.runners.Suite.runChild(Suite.java:128) at org.junit.runners.Suite.runChild(Suite.java:24) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222) at org.junit.runners.ParentRunner.run(ParentRunner.java:300) at org.junit.runner.JUnitCore.run(JUnitCore.java:157) at org.junit.runner.JUnitCore.run(JUnitCore.java:136) at org.apache.maven.surefire.junitcore.JUnitCoreWrapper.createReqestAndRun(JUnitCoreWrapper.java:139) at org.apache.maven.surefire.junitcore.JUnitCoreWrapper.executeEager(JUnitCoreWrapper.java:111) at org.apache.maven.surefire.junitcore.JUnitCoreWrapper.execute(JUnitCoreWrapper.java:84) at org.apache.maven.surefire.junitcore.JUnitCoreProvider.invoke(JUnitCoreProvider.java:138) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:601) at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray2(ReflectionUtils.java:208) at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:159) at org.apache.maven.surefire.booter.ProviderFactory.invokeProvider(ProviderFactory.java:87) at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:153) at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:95) ]]>

Guidelines: TestFX initialization process

I wrote some tests for find() et al and needed to create a custom Application in the test class. Here are some guidelines for the initialization process.

  1. You need to create Nodes inside the JavaFX Application Thread.
  2. You need to initialize the Toolkit by creating and launching a custom Application class.
  3. You need to launch your custom Application class in a new Thread.
  4. You need not to launch your custom Application class more than once.
  5. You need to wait until start() of your Application provides the primaryStage.
  6. You need to retrieve the primaryStage only once per Application.
  7. You can change the Scene of primaryStage multiple times.
  8. You need to show() your primaryStage in order to retrieve Nodes from it.
  9. You can create multiple custom Stages independent of primaryStage.

We should make it easy for the user to setup custom Applications in his/her tests. The user should be able to decide, if he/she wants the setup of the Scene of primaryStage per test or per test class.

Symptomes:

  • (1) java.lang.IllegalStateException: Not on FX application thread; currentThread = main
  • (2) java.lang.IllegalStateException: Toolkit not initialized
  • (3) The launch will block the current thread.
  • (4) java.lang.IllegalStateException: Application launch must not be called more than once
  • (5) java.lang.IllegalStateException: Toolkit not initialized

common predicate methods

assertThat( ".node", predicate )
waitFor( predicate )

Predicate p = hasLabel("Foo")
Predicate p = nodeExists("Foo")
etc.

Scroll to nodes

If a node is out of view, because it is inside a scroll container, TestFX clicks it anyway, so the click ends up on a different node.

The situation should be detected automatically and TestFX should either throw an exception (like it does with invisible nodes, so it should treat those nodes as hidden and not even find them). An even better solution would be, if TestFX could move the scrollbar (or alternatively do the scrolling manually) and then click the node as expected.

Menu items un-clickable

I noticed this today when working my way through various tests of my UI, due to the fact that although a MenuBar is a direct descendant of Node, and therefore clickable, it's respective MenuItems (namely, menus'), do NOT have any ancestral relationship to Node, thus making them unclickable by a class that extends GuiTest, even if they have a public id available. Obviously you could first click the menubar (which always goes to the same location) and then move the cursor a set number of pixels left and right, but this doesn't lend itself very well to future expansion, or change in the layout of the UI.

related FXML that would create an 'unclickable' menu inside of a menubar:

     <MenuBar id="menuBar" fx:id="menu" depthTest="DISABLE"           focusTraversable="true" pickOnBounds="true" prefHeight="24.0" prefWidth="565.0">
  <menus>
    <Menu id="menu_file" text="File" fx:id="menu_file">
      <items>
        <MenuItem id="close1" onAction="#stage_closer" text="Close" />
      </items>
    </Menu>
    <Menu id="f_m_win" text="Windows" fx:id="f_m_win" />
    <Menu id="menu_data" text="Data">
      <items>
        <MenuItem id="menuItem1" onAction="#metadata_shower" text="Metadata" />
      </items>
    </Menu>
    <Menu id="menu_tools" fx:id="menu_tools" mnemonicParsing="true" text="Tools">
      <items>

        <MenuItem id="menu_tools_read_file" mnemonicParsing="false" onAction="#open_fileChooser_window" text="Read File" />
      </items>
    </Menu>

Ignore hidden nodes

While it makes some sense to also return hidden nodes with findAll() and maybe even with find(), I'd expect click("SomeLabel") to always click the one that is currently visible (even though there might be more controls labeled "SomeLabel", maybe on some hidden tab or so). Clicking a hidden node leads to a click completely outside of the window (negative coordinates), which accidently closed my IDE :-).

Possible solutions:

  • Change findAll() to only search the visible part of the tree.
  • Have a second version of findAll() and find() to do that - and use this one from mouse-actions like click() or pointFor().
  • Leave findAll() as it is, but have find() query all results and "sort" them by visibility, and then return the first one, so that visible results are preferred. Instead of sorting, one might also use Iterables.tryFind() or so.
  • There certainly are other solutions to that.

Stop printing component?

Please stop printing
component?
component?
component?
component?
component?
component?
component?
component?
component?
component?
component?
component?
component?
component?
component?
component?
component?
component?
component?
component?
component?
component?
component?
component?
component?
component?
component?
component?
component?
component?
component?
While waiting for a component to show up.

Can't build from source

Attempt to compile and test the project from a fresh checkout results in error

$ mvn -version
Apache Maven 3.1.0 (893ca28a1da9d5f51ac03827af98bb730128f9f2; 2013-06-28 04:15:32+0200)
Maven home: /usr/local/maven
Java version: 1.8.0-ea, vendor: Oracle Corporation
Java home: /Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home/jre
Default locale: en_US, platform encoding: UTF-8
OS name: "mac os x", version: "10.7.5", arch: "x86_64", family: "mac"

This happens with JDK8-b106. Attempting with JDK7u40 leads to unresolved classes due to the JavaFX jar not being available in the classpath (hence why the javafx-maven-plugin requires a hack).

Full error when building with JDK8 follows

mvn test
[INFO] Scanning for projects...
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building TestFX 2.7.2-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ testFx ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 1 resource
[INFO] 
[INFO] --- maven-compiler-plugin:2.3.1:compile (default-compile) @ testFx ---
[WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent!
[INFO] Compiling 13 source files to /Users/aalmiray/dev/testfx/target/classes
[INFO] 
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ testFx ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 1 resource
[INFO] 
[INFO] --- maven-compiler-plugin:2.3.1:testCompile (default-testCompile) @ testFx ---
[WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent!
[INFO] Compiling 3 source files to /Users/aalmiray/dev/testfx/target/test-classes
[INFO] 
[INFO] --- maven-surefire-plugin:2.14.1:test (default-test) @ testFx ---
[WARNING] The parameter forkMode is deprecated since version 2.14. Use forkCount and reuseForks instead.
[INFO] Surefire report directory: /Users/aalmiray/dev/testfx/target/surefire-reports

-------------------------------------------------------
 T E S T S
-------------------------------------------------------

-------------------------------------------------------
 T E S T S
-------------------------------------------------------

Results :

Tests run: 0, Failures: 0, Errors: 0, Skipped: 0

[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.030s
[INFO] Finished at: Wed Sep 11 22:42:54 CEST 2013
[INFO] Final Memory: 22M/213M
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:2.14.1:test (default-test) on project testFx: Execution default-test of goal org.apache.maven.plugins:maven-surefire-plugin:2.14.1:test failed: java.lang.NoClassDefFoundError: javafx/beans/value/WritableValue; nested exception is java.lang.NoClassDefFoundError: javafx/beans/value/WritableValue: javafx.beans.value.WritableValue -> [Help 1]
[ERROR] 
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR] 
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/PluginExecutionException

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.