diff --git a/richtextfx/build.gradle b/richtextfx/build.gradle index 2aefded09..d53d1a313 100644 --- a/richtextfx/build.gradle +++ b/richtextfx/build.gradle @@ -20,7 +20,6 @@ testSets { check.dependsOn integrationTest integrationTest.mustRunAfter test -integrationTest.systemProperty "testfx.robot", "glass" if (gradle.gradleVersion.substring(0, 1) >= "4") { // required for Gradle 4 to see custom integrationTest test suite integrationTest.testClassesDirs = sourceSets.integrationTest.output.classesDirs @@ -106,6 +105,35 @@ test { } } +integrationTest { + testLogging { + // Fancy formatting from http://stackoverflow.com/a/36130467/3634630 + // set options for log level LIFECYCLE + events TestLogEvent.PASSED, TestLogEvent.SKIPPED, + TestLogEvent.FAILED, TestLogEvent.STANDARD_OUT + showExceptions true + exceptionFormat TestExceptionFormat.FULL + showCauses true + showStackTraces true + + // set options for log level DEBUG and INFO + debug { + events TestLogEvent.STARTED, TestLogEvent.PASSED, + TestLogEvent.SKIPPED, TestLogEvent.FAILED, + TestLogEvent.STANDARD_OUT, TestLogEvent.STANDARD_ERROR + } + info.events = debug.events + afterSuite { desc, result -> + if (!desc.parent) { // will match the outermost suite + def output = "Results: ${result.resultType} (${result.testCount} tests, ${result.successfulTestCount} successes, ${result.failedTestCount} failures, ${result.skippedTestCount} skipped)" + def startItem = '| ', endItem = ' |' + def repeatLength = startItem.length() + output.length() + endItem.length() + println('\n' + ('-' * repeatLength) + '\n' + startItem + output + endItem + '\n' + ('-' * repeatLength)) + } + } + } +} + task javadocJar(type: Jar, dependsOn: javadoc) { classifier = 'javadoc' from 'build/docs/javadoc' diff --git a/richtextfx/src/integrationTest/java/org/fxmisc/richtext/InlineCssTextAreaAppTest.java b/richtextfx/src/integrationTest/java/org/fxmisc/richtext/InlineCssTextAreaAppTest.java index 81a171fbd..249b63265 100644 --- a/richtextfx/src/integrationTest/java/org/fxmisc/richtext/InlineCssTextAreaAppTest.java +++ b/richtextfx/src/integrationTest/java/org/fxmisc/richtext/InlineCssTextAreaAppTest.java @@ -1,41 +1,21 @@ package org.fxmisc.richtext; import javafx.geometry.Pos; -import javafx.scene.Node; import javafx.scene.Scene; -import javafx.scene.control.ContextMenu; -import javafx.scene.control.MenuItem; import javafx.scene.input.MouseButton; import javafx.stage.Stage; -import javafx.stage.Window; import org.testfx.api.FxRobotInterface; -import org.testfx.framework.junit.ApplicationTest; import org.testfx.service.query.PointQuery; -import static org.junit.Assume.assumeTrue; - /** * TestFX tests should subclass this if it needs to run tests on a simple area. Any view-related API needs to be * wrapped in a {@link #interact(Runnable)} call, but model API does not need to be wrapped in it. */ -public class InlineCssTextAreaAppTest extends ApplicationTest { - - static { - String osName = System.getProperty("os.name").toLowerCase(); - - isWindows = osName.startsWith("win"); - isMac = osName.startsWith("mac"); - isLinux = osName.startsWith("linux"); - } - - public static final boolean isWindows; - public static final boolean isMac; - public static final boolean isLinux; +public class InlineCssTextAreaAppTest extends RichTextFXTestBase { public Stage stage; public Scene scene; public InlineCssTextArea area; - public ContextMenu menu; @Override public void start(Stage stage) throws Exception { @@ -48,28 +28,10 @@ public void start(Stage stage) throws Exception { stage.setHeight(400); stage.show(); - menu = new ContextMenu(new MenuItem("A menu item")); - area.setContextMenu(menu); - // offset needs to be 5+ to prevent test failures - area.setContextMenuXOffset(30); - area.setContextMenuYOffset(30); - // so tests don't need to do this themselves area.requestFocus(); } - public final PointQuery position(Scene scene, Pos pos, double xOffset, double yOffset) { - return point(scene).atPosition(pos).atOffset(xOffset, yOffset); - } - - public final PointQuery position(Window window, Pos pos, double xOffset, double yOffset) { - return point(window).atPosition(pos).atOffset(xOffset, yOffset); - } - - public final PointQuery position(Node node, Pos pos, double xOffset, double yOffset) { - return point(node).atPosition(pos).atOffset(xOffset, yOffset); - } - public final PointQuery position(Pos pos, double xOffset, double yOffset) { return position(area, pos, xOffset, yOffset); } @@ -98,51 +60,4 @@ public final FxRobotInterface rightClickOnFirstLine() { return clickOnFirstLine(MouseButton.SECONDARY); } - /** - * If not on Windows environment, calling this in @Before method will skip the entire test suite whereas calling - * this in @Test will skip just that test method - */ - public final void run_only_on_windows() { - assumeTrue(isWindows); - } - - /** - * If not on Linux environment, calling this in @Before method will skip the entire test suite whereas calling - * this in @Test will skip just that test method - */ - public final void run_only_on_linux() { - assumeTrue(isLinux); - } - - /** - * If not on Mac environment, calling this in @Before method will skip the entire test suite whereas calling - * this in @Test will skip just that test method - */ - public final void run_only_on_mac() { - assumeTrue(isMac); - } - - /** - * If on Windows environment, calling this in @Before method will skip the entire test suite whereas calling - * this in @Test will skip just that test method - */ - public final void skip_if_on_windows() { - assumeTrue(!isWindows); - } - - /** - * If on Linux environment, calling this in @Before method will skip the entire test suite whereas calling - * this in @Test will skip just that test method - */ - public final void skip_if_on_linux() { - assumeTrue(!isLinux); - } - - /** - * If on Mac environment, calling this in @Before method will skip the entire test suite whereas calling - * this in @Test will skip just that test method - */ - public final void skip_if_on_mac() { - assumeTrue(!isMac); - } } diff --git a/richtextfx/src/integrationTest/java/org/fxmisc/richtext/RichTextFXTestBase.java b/richtextfx/src/integrationTest/java/org/fxmisc/richtext/RichTextFXTestBase.java new file mode 100644 index 000000000..67a20f8bf --- /dev/null +++ b/richtextfx/src/integrationTest/java/org/fxmisc/richtext/RichTextFXTestBase.java @@ -0,0 +1,117 @@ +package org.fxmisc.richtext; + +import javafx.geometry.Pos; +import javafx.scene.Node; +import javafx.scene.Scene; +import javafx.stage.Window; +import org.testfx.framework.junit.ApplicationTest; +import org.testfx.service.query.PointQuery; + +import static org.junit.Assume.assumeTrue; + +/** + * Provides useful static fields and helper methods for RichTextFX integration tests. + * + * + */ +public abstract class RichTextFXTestBase extends ApplicationTest { + + static { + String osName = System.getProperty("os.name").toLowerCase(); + + IS_WINDOWS = osName.startsWith("win"); + IS_MAC = osName.startsWith("mac"); + IS_LINUX = osName.startsWith("linux"); + } + + /* *********************************************** * + * OS-RELATED + * *********************************************** */ + + public static final boolean IS_WINDOWS; + public static final boolean IS_MAC; + public static final boolean IS_LINUX; + + /** + * If not on Windows environment, calling this in @Before method will skip the entire test suite whereas calling + * this in @Test will skip just that test method + */ + public final void run_only_on_windows() { + assumeTrue(IS_WINDOWS); + } + + /** + * If not on Linux environment, calling this in @Before method will skip the entire test suite whereas calling + * this in @Test will skip just that test method + */ + public final void run_only_on_linux() { + assumeTrue(IS_LINUX); + } + + /** + * If not on Mac environment, calling this in @Before method will skip the entire test suite whereas calling + * this in @Test will skip just that test method + */ + public final void run_only_on_mac() { + assumeTrue(IS_MAC); + } + + /** + * If on Windows environment, calling this in @Before method will skip the entire test suite whereas calling + * this in @Test will skip just that test method + */ + public final void skip_if_on_windows() { + assumeTrue(!IS_WINDOWS); + } + + /** + * If on Linux environment, calling this in @Before method will skip the entire test suite whereas calling + * this in @Test will skip just that test method + */ + public final void skip_if_on_linux() { + assumeTrue(!IS_LINUX); + } + + /** + * If on Mac environment, calling this in @Before method will skip the entire test suite whereas calling + * this in @Test will skip just that test method + */ + public final void skip_if_on_mac() { + assumeTrue(!IS_MAC); + } + + /* *********************************************** * + * Position-Related + * *********************************************** */ + + /** + * Returns a specific position in the scene, starting at {@code pos} and offsetting from that place by + * {@code xOffset} and {@code yOffset} + */ + public final PointQuery position(Scene scene, Pos pos, double xOffset, double yOffset) { + return point(scene).atPosition(pos).atOffset(xOffset, yOffset); + } + + /** + * Returns a specific position in the window, starting at {@code pos} and offsetting from that place by + * {@code xOffset} and {@code yOffset} + */ + public final PointQuery position(Window window, Pos pos, double xOffset, double yOffset) { + return point(window).atPosition(pos).atOffset(xOffset, yOffset); + } + + /** + * Returns a specific position in the node, starting at {@code pos} and offsetting from that place by + * {@code xOffset} and {@code yOffset} + */ + public final PointQuery position(Node node, Pos pos, double xOffset, double yOffset) { + return point(node).atPosition(pos).atOffset(xOffset, yOffset); + } +} diff --git a/richtextfx/src/integrationTest/java/org/fxmisc/richtext/api/CaretTests.java b/richtextfx/src/integrationTest/java/org/fxmisc/richtext/api/CaretTests.java index 4f5d47e56..ac1ebcd53 100644 --- a/richtextfx/src/integrationTest/java/org/fxmisc/richtext/api/CaretTests.java +++ b/richtextfx/src/integrationTest/java/org/fxmisc/richtext/api/CaretTests.java @@ -11,24 +11,30 @@ public class CaretTests extends InlineCssTextAreaAppTest { + private static final String FIFTY_PARS_OF_TEXT; + + static { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 50; i++) { + sb.append(i).append("\n"); + } + sb.append(50); + FIFTY_PARS_OF_TEXT = sb.toString(); + } + @Override public void start(Stage stage) throws Exception { super.start(stage); // insure caret is always visible area.setShowCaret(Caret.CaretVisibility.ON); - - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < 50; i++) { - sb.append(i).append("\n"); - } - area.replaceText(sb.toString()); + area.replaceText(FIFTY_PARS_OF_TEXT); area.moveTo(0); area.showParagraphAtTop(0); } @Test - public void testMoveCaretAndFollowIt() { + public void caret_bounds_are_present_after_moving_caret_and_following_it() { assertTrue(area.getCaretBounds().isPresent()); // move caret outside of viewport @@ -43,7 +49,7 @@ public void testMoveCaretAndFollowIt() { } @Test - public void testMoveCaretWithoutFollowingIt() { + public void caret_bounds_are_absent_after_moving_caret_without_following_it() { assertTrue(area.getCaretBounds().isPresent()); // move caret outside of viewport diff --git a/richtextfx/src/integrationTest/java/org/fxmisc/richtext/api/CharacterBoundsTest.java b/richtextfx/src/integrationTest/java/org/fxmisc/richtext/api/CharacterBoundsTest.java index 72813fab0..580f57b35 100644 --- a/richtextfx/src/integrationTest/java/org/fxmisc/richtext/api/CharacterBoundsTest.java +++ b/richtextfx/src/integrationTest/java/org/fxmisc/richtext/api/CharacterBoundsTest.java @@ -16,7 +16,7 @@ public void start(Stage stage) throws Exception { } @Test - public void selectionBoundsUnchangedWhenCallGetCharacterBounds() { + public void selection_bounds_are_unchanged_when_call_getCharacterBounds() { area.selectAll(); Bounds bounds = area.getSelectionBounds().get(); diff --git a/richtextfx/src/integrationTest/java/org/fxmisc/richtext/api/ClipboardTests.java b/richtextfx/src/integrationTest/java/org/fxmisc/richtext/api/ClipboardTests.java deleted file mode 100644 index 127ab3744..000000000 --- a/richtextfx/src/integrationTest/java/org/fxmisc/richtext/api/ClipboardTests.java +++ /dev/null @@ -1,49 +0,0 @@ -package org.fxmisc.richtext.api; - -import com.nitorcreations.junit.runners.NestedRunner; -import javafx.scene.Scene; -import javafx.stage.Stage; -import org.fxmisc.flowless.VirtualizedScrollPane; -import org.fxmisc.richtext.CodeArea; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.testfx.framework.junit.ApplicationTest; - -import static javafx.scene.input.KeyCode.*; - -@RunWith(NestedRunner.class) -public class ClipboardTests { - - public class CopyTests extends ApplicationTest { - - CodeArea area; - - @Override - public void start(Stage primaryStage) throws Exception { - area = new CodeArea("abc\ndef\nghi"); - VirtualizedScrollPane vsPane = new VirtualizedScrollPane<>(area); - - Scene scene = new Scene(vsPane, 400, 400); - primaryStage.setScene(scene); - primaryStage.show(); - } - - - public class WhenUserMakesSelectionEndingInNewLineCharacter { - - @Before - public void setup() { - area.selectRange(2, 4); - } - - @Test - public void copyingShouldNotThrowException() { - push(CONTROL, C); - - push(CONTROL, V); - } - } - } - -} diff --git a/richtextfx/src/integrationTest/java/org/fxmisc/richtext/api/HitTests.java b/richtextfx/src/integrationTest/java/org/fxmisc/richtext/api/HitTests.java index 9aa587174..4ec7ef34c 100644 --- a/richtextfx/src/integrationTest/java/org/fxmisc/richtext/api/HitTests.java +++ b/richtextfx/src/integrationTest/java/org/fxmisc/richtext/api/HitTests.java @@ -5,6 +5,7 @@ import javafx.geometry.Insets; import javafx.geometry.Point2D; import javafx.geometry.Pos; +import javafx.stage.Stage; import org.fxmisc.richtext.InlineCssTextAreaAppTest; import org.fxmisc.richtext.model.NavigationActions; import org.junit.Before; @@ -19,15 +20,39 @@ @RunWith(NestedRunner.class) public class HitTests extends InlineCssTextAreaAppTest { + private static final String ALPHABET = "abcdefghijklmnopqrstuvwxyz"; + private static final String FIFTY_PARS; + private static final double PADDING_AMOUNT = 20; + + static { + int totalPars = 50; + int indexLimit = totalPars - 1; + StringBuilder sb = new StringBuilder(); + Consumer appendParagraph = i -> sb.append("Par #").append(i).append(" ").append(ALPHABET); + for (int i = 0; i < indexLimit; i++) { + appendParagraph.accept(i); + sb.append("\n"); + } + appendParagraph.accept(indexLimit); + FIFTY_PARS = sb.toString(); + } + + @Override + public void start(Stage stage) throws Exception { + super.start(stage); + + // insure stage width doesn't change irregardless of changes in superclass' start method + stage.setWidth(400); + stage.setHeight(400); + } + private void moveCaretToAreaEnd() { area.moveTo(area.getLength()); } - public class WhenAreaIsPadded { + public class When_Area_Is_Padded { - double paddingAmount = 20; - - public class AndHitsOccurOutsideArea { + public class And_Hits_Occur_Outside_Area { String text = "text"; String fullText = text + "\n" + text; @@ -38,8 +63,8 @@ public void setup() { } @Test - public void clickingInTopPaddingMovesCaretToTopLine() { - interact(() -> area.setPadding(new Insets(paddingAmount, 0, 0, 0))); + public void clicking_in_top_padding_moves_caret_to_top_line() { + interact(() -> area.setPadding(new Insets(PADDING_AMOUNT, 0, 0, 0))); moveCaretToAreaEnd(); moveTo(position(Pos.TOP_LEFT, 1, 2)).clickOn(PRIMARY); @@ -51,8 +76,8 @@ public void clickingInTopPaddingMovesCaretToTopLine() { } @Test - public void clickingInLeftPaddingMovesCaretToBeginningOfLineOnSingleLineParagraph() { - interact(() -> area.setPadding(new Insets(0, 0, 0, paddingAmount))); + public void clicking_in_left_padding_moves_caret_to_beginning_of_line_on_single_line_paragraph() { + interact(() -> area.setPadding(new Insets(0, 0, 0, PADDING_AMOUNT))); moveCaretToAreaEnd(); moveTo(position(Pos.TOP_LEFT, 1, 1)).clickOn(PRIMARY); @@ -60,9 +85,9 @@ public void clickingInLeftPaddingMovesCaretToBeginningOfLineOnSingleLineParagrap } @Test - public void clickingInRightPaddingMovesCaretToEndOfLineOnSingleLineParagraph() { + public void clicking_in_right_padding_moves_caret_to_end_of_line_on_single_line_paragraph() { interact(() -> { - area.setPadding(new Insets(0, paddingAmount, 0, 0)); + area.setPadding(new Insets(0, PADDING_AMOUNT, 0, 0)); area.moveTo(0); // insure we're scrolled all the way to the right @@ -74,9 +99,9 @@ public void clickingInRightPaddingMovesCaretToEndOfLineOnSingleLineParagraph() { } @Test - public void clickingInBottomPaddingMovesCaretToBottomLine() { + public void clicking_in_bottom_padding_moves_caret_to_bottom_line() { interact(() -> { - area.setPadding(new Insets(0, 0, paddingAmount, 0)); + area.setPadding(new Insets(0, 0, PADDING_AMOUNT, 0)); area.moveTo(0); // insure we're scrolled all the way to the bottom @@ -89,35 +114,19 @@ public void clickingInBottomPaddingMovesCaretToBottomLine() { } - public class AndHitsOccurInsideArea { - - String text = "abcdefghijklmnopqrstuvwxyz"; - String fullText; - - { - int totalPars = 50; - int indexLimit = totalPars - 1; - StringBuilder sb = new StringBuilder(); - Consumer appendParagraph = i -> sb.append("Par #").append(i).append(" ").append(text); - for (int i = 0; i < indexLimit; i++) { - appendParagraph.accept(i); - sb.append("\n"); - } - appendParagraph.accept(indexLimit); - fullText = sb.toString(); - } + public class And_Hits_Occur_Inside_Area { @Before public void setup() { interact(() -> { - area.replaceText(fullText); - area.setPadding(new Insets(paddingAmount)); + area.replaceText(FIFTY_PARS); + area.setPadding(new Insets(PADDING_AMOUNT)); area.setStyle("-fx-font-family: monospace; -fx-font-size: 12pt;"); }); } @Test - public void clickingCharacterShouldMoveCaretToThatPosition() { + public void clicking_character_should_move_caret_to_that_position() { int start = area.getAbsolutePosition(3, 8); Bounds b = area.getCharacterBoundsOnScreen(start, start + 1).get(); moveTo(b).clickOn(PRIMARY); @@ -125,11 +134,7 @@ public void clickingCharacterShouldMoveCaretToThatPosition() { } @Test - public void prevPageMovesCaretToTopOfPage() { - // Mac: failed; Windows: untested - // TODO: test on respective OS and update expected values to be correct - run_only_on_linux(); - + public void prev_page_moves_caret_to_top_of_page() { area.showParagraphAtBottom(area.getParagraphs().size() - 1); // move to last line, column 0 area.moveTo(area.getParagraphs().size() - 1, 0); @@ -140,15 +145,11 @@ public void prevPageMovesCaretToTopOfPage() { }); assertEquals(0, area.getCaretColumn()); - assertEquals(32, area.getCurrentParagraph()); + assertEquals(area.firstVisibleParToAllParIndex(), area.getCurrentParagraph()); } @Test - public void nextPageMovesCaretToBottomOfPage() { - // Mac: failed; Windows: untested - // TODO: test on respective OS and update expected values to be correct - run_only_on_linux(); - + public void next_page_moves_caret_to_bottom_of_page() { area.showParagraphAtTop(0); area.moveTo(0); @@ -158,37 +159,19 @@ public void nextPageMovesCaretToBottomOfPage() { }); assertEquals(0, area.getCaretColumn()); - assertEquals(17, area.getCurrentParagraph()); + assertEquals(area.lastVisibleParToAllParIndex(), area.getCurrentParagraph()); } } } - public class WhenParagraphBoxIsPadded { - - double paddingAmount = 20; - - String text = "abcdefghijklmnopqrstuvwxyz"; - String fullText; - - { - int totalPars = 50; - int indexLimit = totalPars - 1; - StringBuilder sb = new StringBuilder(); - Consumer appendParagraph = i -> sb.append("Par #").append(i).append(" ").append(text); - for (int i = 0; i < indexLimit; i++) { - appendParagraph.accept(i); - sb.append("\n"); - } - appendParagraph.accept(indexLimit); - fullText = sb.toString(); - } + public class When_ParagraphBox_Is_Padded { @Before public void setup() { interact(() -> { - area.replaceText(fullText); + area.replaceText(FIFTY_PARS); area.setStyle("-fx-font-family: monospace; -fx-font-size: 12pt;"); scene.getStylesheets().add(HitTests.class.getResource("padded-paragraph-box.css").toExternalForm()); }); @@ -201,20 +184,20 @@ private void runTest() { assertEquals(start, area.getCaretPosition()); } - public class AndAreaIsPadded { + public class And_Area_Is_Padded { @Test - public void clickingCharacterShouldMoveCaretToThatPosition() { - interact(() -> area.setPadding(new Insets(paddingAmount))); + public void clicking_character_should_move_caret_to_that_position() { + interact(() -> area.setPadding(new Insets(PADDING_AMOUNT))); runTest(); } } - public class AndAreaIsNotPadded { + public class And_Area_Is_Not_Padded { @Test - public void clickingCharacterShouldMoveCaretToThatPosition() { + public void clicking_character_should_move_caret_to_that_position() { runTest(); } diff --git a/richtextfx/src/integrationTest/java/org/fxmisc/richtext/api/MiscellaneousTests.java b/richtextfx/src/integrationTest/java/org/fxmisc/richtext/api/MiscellaneousTests.java deleted file mode 100644 index 2d4a4e106..000000000 --- a/richtextfx/src/integrationTest/java/org/fxmisc/richtext/api/MiscellaneousTests.java +++ /dev/null @@ -1,38 +0,0 @@ -package org.fxmisc.richtext.api; - -import com.nitorcreations.junit.runners.NestedRunner; -import javafx.stage.Stage; -import org.fxmisc.richtext.InlineCssTextAreaAppTest; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import static javafx.scene.input.KeyCode.DELETE; - -@RunWith(NestedRunner.class) -public class MiscellaneousTests { - - public class WhenAreaEndsWithEmptyLine extends InlineCssTextAreaAppTest { - - @Override - public void start(Stage stage) throws Exception { - super.start(stage); - area.replaceText(0, 0, "abc\n"); - } - - public class AndAllTextIsSelected { - - @Before - public void selectAllText() { - interact(() -> area.selectAll()); - } - - - @Test - public void pressingDeleteShouldNotThrowException() { - push(DELETE); - } - - } - } -} diff --git a/richtextfx/src/integrationTest/java/org/fxmisc/richtext/api/MouseOverTextDelayTests.java b/richtextfx/src/integrationTest/java/org/fxmisc/richtext/api/MouseOverTextDelayTests.java index 986eac5c3..d526c290d 100644 --- a/richtextfx/src/integrationTest/java/org/fxmisc/richtext/api/MouseOverTextDelayTests.java +++ b/richtextfx/src/integrationTest/java/org/fxmisc/richtext/api/MouseOverTextDelayTests.java @@ -50,7 +50,7 @@ private void setDelay(long milliseconds) { } @Test - public void nullDelayNeverFires() { + public void null_delay_never_fires() { setDelay(null); moveTo(firstLineOfArea()).sleep(300); @@ -60,7 +60,7 @@ public void nullDelayNeverFires() { @Ignore("END events are fired multiple times when BEGIN event hasn't yet fired") @Test - public void eventsFireAfterDelayAndPostMove() { + public void events_fire_after_delay_and_post_move() { setDelay(100); moveTo(firstLineOfArea()).sleep(300); @@ -76,7 +76,7 @@ public void eventsFireAfterDelayAndPostMove() { @Ignore("setting delay while mouse is over text fires END event when BEGIN event hasn't yet fired") @Test - public void settingDelayWhileMouseOverTextDoesNotFireEvent() { + public void setting_delay_while_mouse_is_over_text_does_not_fire_event() { setDelay(null); moveTo(firstLineOfArea()).sleep(300); @@ -94,7 +94,7 @@ public void settingDelayWhileMouseOverTextDoesNotFireEvent() { @Ignore("this test is only important when above two tests get fixed") @Test - public void settingDelayBeforeEndFiresPreventsEndFromFiring() { + public void setting_delay_before_end_fires_prevents_end_from_firing() { setDelay(100); moveTo(firstLineOfArea()).sleep(200); diff --git a/richtextfx/src/integrationTest/java/org/fxmisc/richtext/api/ParagraphLinesCountTests.java b/richtextfx/src/integrationTest/java/org/fxmisc/richtext/api/ParagraphLinesCountTests.java index f8d42843e..f4ea1d3e0 100644 --- a/richtextfx/src/integrationTest/java/org/fxmisc/richtext/api/ParagraphLinesCountTests.java +++ b/richtextfx/src/integrationTest/java/org/fxmisc/richtext/api/ParagraphLinesCountTests.java @@ -9,7 +9,7 @@ public class ParagraphLinesCountTests extends InlineCssTextAreaAppTest { @Test - public void multiLineReturnsCorrectCount() { + public void multi_line_returns_correct_count() { String[] lines = { "01 02 03 04 05", "11 12 13 14 15", @@ -25,7 +25,7 @@ public void multiLineReturnsCorrectCount() { } @Test - public void singleLineReturnsOne() { + public void single_line_returns_one() { interact(() -> area.replaceText("some text")); assertFalse(area.isWrapText()); diff --git a/richtextfx/src/integrationTest/java/org/fxmisc/richtext/api/UndoManagerTests.java b/richtextfx/src/integrationTest/java/org/fxmisc/richtext/api/UndoManagerTests.java index a9c162efc..3187e314c 100644 --- a/richtextfx/src/integrationTest/java/org/fxmisc/richtext/api/UndoManagerTests.java +++ b/richtextfx/src/integrationTest/java/org/fxmisc/richtext/api/UndoManagerTests.java @@ -9,7 +9,7 @@ public class UndoManagerTests extends InlineCssTextAreaAppTest { @Test - public void preventMergeOfIncomingChangeAfterPeriodOfUserInactivity() { + public void incoming_change_is_not_merged_after_period_of_user_inactivity() { String text1 = "text1"; String text2 = "text2"; diff --git a/richtextfx/src/integrationTest/java/org/fxmisc/richtext/keyboard/CutCopyPasteTests.java b/richtextfx/src/integrationTest/java/org/fxmisc/richtext/keyboard/CutCopyPasteTests.java index b47af6c71..bd4a65657 100644 --- a/richtextfx/src/integrationTest/java/org/fxmisc/richtext/keyboard/CutCopyPasteTests.java +++ b/richtextfx/src/integrationTest/java/org/fxmisc/richtext/keyboard/CutCopyPasteTests.java @@ -1,14 +1,17 @@ package org.fxmisc.richtext.keyboard; import com.nitorcreations.junit.runners.NestedRunner; +import javafx.scene.Scene; import javafx.scene.input.Clipboard; import javafx.scene.input.ClipboardContent; import javafx.stage.Stage; +import org.fxmisc.flowless.VirtualizedScrollPane; +import org.fxmisc.richtext.CodeArea; import org.fxmisc.richtext.InlineCssTextAreaAppTest; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; +import org.testfx.framework.junit.ApplicationTest; import static javafx.scene.input.KeyCode.*; import static junit.framework.TestCase.assertEquals; @@ -32,7 +35,7 @@ public void start(Stage stage) throws Exception { area.replaceText(fullText); } - public class WhenNothingIsSelected { + public class When_Nothing_Is_Selected { @Before public void insureSelectionIsEmpty() { @@ -40,7 +43,7 @@ public void insureSelectionIsEmpty() { assertTrue(area.getSelectedText().isEmpty()); } - public class NothingIsStoredInClipboardWhenCopyVia { + public class Nothing_Is_Stored_In_Clipboard_When_Copy_Via { private void runAssert() { interact(() -> assertFalse(Clipboard.getSystemClipboard().hasString())); @@ -59,14 +62,14 @@ public void copy() { } @Test - public void shortcut_C() { + public void shortcut_c() { press(SHORTCUT, C); runAssert(); } @Test - public void shortcut_Insert() { + public void shortcut_insert() { press(SHORTCUT, INSERT); runAssert(); @@ -74,7 +77,7 @@ public void shortcut_Insert() { } - public class NothingIsRemovedInAreaWhenCutVia { + public class Nothing_Is_Removed_In_Area_When_Cut_Via { private void runAssert() { assertEquals(fullText, area.getText()); @@ -88,14 +91,14 @@ public void cut() { } @Test - public void shortcut_X() { + public void shortcut_x() { press(SHORTCUT, X); runAssert(); } @Test - public void shift_Delete() { + public void shift_delete() { press(SHIFT, DELETE); runAssert(); @@ -103,7 +106,7 @@ public void shift_Delete() { } - public class TextIsInsertedInAreaWhenPasteVia { + public class Text_Is_Inserted_In_Area_When_Paste_Via { private void runAssert() { assertEquals(beginning + text + middle + end, area.getText()); @@ -131,14 +134,14 @@ public void paste() { } @Test - public void shortcut_V() { + public void shortcut_v() { press(SHORTCUT, V); runAssert(); } @Test - public void shift_Insert() { + public void shift_insert() { press(SHIFT, INSERT); runAssert(); @@ -146,18 +149,19 @@ public void shift_Insert() { } } - public class WhenTextIsSelected { + public class When_Text_Is_Selected { int startMiddle = beginning.length(); int endMiddle = startMiddle + middle.length(); @Before - public void selectMiddle() { + public void selectMiddleAndClearClipboard() { area.selectRange(startMiddle, endMiddle); assertEquals(middle, area.getSelectedText()); + interact(() -> Clipboard.getSystemClipboard().clear()); } - public class SelectionIsStoredInClipboardWhenCopyVia { + public class Selection_Is_Stored_In_Clipboard_When_Copy_Via { private void runAssert() { interact(() -> { @@ -168,20 +172,25 @@ private void runAssert() { @Test public void copy() { + // this test fails on Linux; Windows is untested + // so for now, only run on Mac + // TODO: update if test succeeds on Windows, too + run_only_on_mac(); + press(COPY); runAssert(); } @Test - public void shortcut_C() { + public void shortcut_c() { press(SHORTCUT, C); runAssert(); } @Test - public void shortcut_Insert() { + public void shortcut_insert() { press(SHORTCUT, INSERT); runAssert(); @@ -189,7 +198,7 @@ public void shortcut_Insert() { } - public class SelectionIsRemovedAndStoredInClipboardWhenCutVia { + public class Selection_Is_Removed_And_Stored_In_Clipboard_When_Cut_Via { private void runAssert() { assertEquals(beginning + end, area.getText()); @@ -200,7 +209,7 @@ private void runAssert() { } @Test - public void cut() { + public void cut() { // this test fails on Linux; Windows is untested // so for now, only run on Mac // TODO: update if test succeeds on Windows, too @@ -212,14 +221,14 @@ public void cut() { } @Test - public void shortcut_X() { + public void shortcut_x() { press(SHORTCUT, X); runAssert(); } @Test - public void shift_Delete() { + public void shift_delete() { press(SHIFT, DELETE); runAssert(); @@ -227,7 +236,7 @@ public void shift_Delete() { } - public class SelectionIsReplacedInAreaWhenPasteVia { + public class Selection_Is_Replaced_In_Area_When_Paste_Via { @Before public void storeTextInClipboard() { @@ -242,9 +251,8 @@ private void runAssert() { assertEquals(beginning + text + end, area.getText()); } - @Ignore("Flaky test when all others of equivalent tests pass") @Test - public void paste() { + public void paste() { // this test fails on Linux; Windows is untested // so for now, only run on Mac // TODO: update if test succeeds on Windows, too @@ -256,14 +264,14 @@ public void paste() { } @Test - public void shortcut_V() { + public void shortcut_v() { press(SHORTCUT, V); runAssert(); } @Test - public void shift_Insert() { + public void shift_insert() { press(SHIFT, INSERT); runAssert(); @@ -271,4 +279,35 @@ public void shift_Insert() { } } + + public class MiscellaneousCases extends ApplicationTest { + + CodeArea area; + + @Override + public void start(Stage primaryStage) throws Exception { + area = new CodeArea("abc\ndef\nghi"); + VirtualizedScrollPane vsPane = new VirtualizedScrollPane<>(area); + + Scene scene = new Scene(vsPane, 400, 400); + primaryStage.setScene(scene); + primaryStage.show(); + } + + public class When_User_Makes_Selection_Ending_In_Newline_Character { + + @Before + public void setup() { + area.selectRange(2, 4); + } + + @Test + public void copying_and_pasting_should_not_throw_exception() { + push(CONTROL, C); + + push(CONTROL, V); + } + } + } + } \ No newline at end of file diff --git a/richtextfx/src/integrationTest/java/org/fxmisc/richtext/keyboard/DeletionTests.java b/richtextfx/src/integrationTest/java/org/fxmisc/richtext/keyboard/DeletionTests.java index 161d0531a..e90c9fcbb 100644 --- a/richtextfx/src/integrationTest/java/org/fxmisc/richtext/keyboard/DeletionTests.java +++ b/richtextfx/src/integrationTest/java/org/fxmisc/richtext/keyboard/DeletionTests.java @@ -3,6 +3,7 @@ import com.nitorcreations.junit.runners.NestedRunner; import javafx.stage.Stage; import org.fxmisc.richtext.InlineCssTextAreaAppTest; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -31,7 +32,7 @@ private String withoutFirstChar(String s) { return s.substring(1); } - public class WhenShortcutIsDown extends InlineCssTextAreaAppTest { + public class When_Shortcut_Is_Down extends InlineCssTextAreaAppTest { @Override public void start(Stage stage) throws Exception { @@ -42,7 +43,7 @@ public void start(Stage stage) throws Exception { } @Test - public void pressingDeleteRemovesNextWordAndSpace() { + public void pressing_delete_removes_next_word_and_space() { area.moveTo(0); int pos = area.getCaretPosition(); @@ -53,7 +54,7 @@ public void pressingDeleteRemovesNextWordAndSpace() { } @Test - public void pressingBackspaceRemovesPreviousWordAndSpace() { + public void pressing_backspace_removes_previous_word_and_space() { area.end(CLEAR); int pos = area.getCaretPosition(); @@ -64,7 +65,7 @@ public void pressingBackspaceRemovesPreviousWordAndSpace() { } } - public class WhenModifiersAreNotDown extends InlineCssTextAreaAppTest { + public class When_No_Modifiers extends InlineCssTextAreaAppTest { @Override public void start(Stage stage) throws Exception { @@ -73,7 +74,7 @@ public void start(Stage stage) throws Exception { } @Test - public void pressingDeleteRemovesNextChar() { + public void pressing_delete_removes_next_char() { area.moveTo(0); int pos = area.getCaretPosition(); @@ -84,7 +85,7 @@ public void pressingDeleteRemovesNextChar() { } @Test - public void pressingBackspaceRemovesPreviousChar() { + public void pressing_backspace_removes_previous_char() { area.end(CLEAR); int pos = area.getCaretPosition(); @@ -95,4 +96,35 @@ public void pressingBackspaceRemovesPreviousChar() { } } + + // miscellaneous cases + + public class When_Area_Ends_With_Empty_Line extends InlineCssTextAreaAppTest { + + @Override + public void start(Stage stage) throws Exception { + super.start(stage); + area.replaceText(0, 0, "abc\n"); + } + + public class And_All_Text_Is_Selected { + + @Before + public void selectAllText() { + interact(() -> area.selectAll()); + } + + + @Test + public void pressing_delete_should_not_throw_exception() { + push(DELETE); + } + + @Test + public void pressing_backspace_should_not_throw_exceptions() { + push(BACK_SPACE); + } + + } + } } diff --git a/richtextfx/src/integrationTest/java/org/fxmisc/richtext/keyboard/NavigationTests.java b/richtextfx/src/integrationTest/java/org/fxmisc/richtext/keyboard/NavigationTests.java deleted file mode 100644 index ae2b67413..000000000 --- a/richtextfx/src/integrationTest/java/org/fxmisc/richtext/keyboard/NavigationTests.java +++ /dev/null @@ -1,633 +0,0 @@ -package org.fxmisc.richtext.keyboard; - -import com.nitorcreations.junit.runners.NestedRunner; -import javafx.geometry.Bounds; -import javafx.stage.Stage; -import org.fxmisc.richtext.GenericStyledArea; -import org.fxmisc.richtext.InlineCssTextAreaAppTest; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.testfx.util.WaitForAsyncUtils; - -import java.util.Arrays; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -import static javafx.scene.input.KeyCode.*; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -@RunWith(NestedRunner.class) -public class NavigationTests { - - private int entityStart(int entityIndex, String[] array) { - if (entityIndex == 0) { - return 0; - } else { - return Arrays.stream(array) - .map(String::length) - .limit(entityIndex) - .reduce(0, (a, b) -> a + b) - + entityIndex; // for delimiter characters - } - } - - private int entityEnd(int entityIndex, String[] array, GenericStyledArea area) { - if (entityIndex == array.length - 1) { - return area.getLength(); - } else { - return entityStart(entityIndex + 1, array) - 1; - } - } - - public class SingleLineTests extends InlineCssTextAreaAppTest { - - String[] words = { "the", "cat", "can", "walk" }; - String fullText = String.join(" ", words); - - private int wordStart(int wordIndex) { - return entityStart(wordIndex, words); - } - - private int wordEnd(int wordIndex) { - return entityEnd(wordIndex, words, area); - } - - private void moveCaretTo(int position) { - area.moveTo(position); - } - - @Override - public void start(Stage stage) throws Exception { - super.start(stage); - stage.setWidth(300); - area.replaceText(fullText); - } - - public class NoModifiers { - - @Test - public void left() { - moveCaretTo(wordStart(1)); - assertTrue(area.getSelectedText().isEmpty()); - - type(LEFT); - - assertEquals(wordEnd(0), area.getCaretPosition()); - assertTrue(area.getSelectedText().isEmpty()); - } - - @Test - public void right() { - moveCaretTo(wordStart(1)); - assertTrue(area.getSelectedText().isEmpty()); - - type(RIGHT); - - assertEquals(wordStart(1) + 1, area.getCaretPosition()); - assertTrue(area.getSelectedText().isEmpty()); - } - - } - - public class ShortcutDown { - - @Before - public void setup() { - press(SHORTCUT); - } - - @Test - public void left() { - moveCaretTo(wordEnd(3)); - assertTrue(area.getSelectedText().isEmpty()); - - // first left goes to boundary of current word - type(LEFT); - - assertEquals(wordStart(3), area.getCaretPosition()); - assertTrue(area.getSelectedText().isEmpty()); - - // second left skips space and goes to end boundary of prev word - type(LEFT); - - assertEquals(wordStart(2), area.getCaretPosition()); - assertTrue(area.getSelectedText().isEmpty()); - } - - @Test - public void right() { - moveCaretTo(wordStart(0)); - assertTrue(area.getSelectedText().isEmpty()); - - // first right goes to boundary of current word - type(RIGHT); - - assertEquals(wordEnd(0), area.getCaretPosition()); - assertTrue(area.getSelectedText().isEmpty()); - - // second right skips space and goes to end boundary of next word - type(RIGHT); - - assertEquals(wordEnd(1), area.getCaretPosition()); - assertTrue(area.getSelectedText().isEmpty()); - } - - @Test - public void a() { - assertTrue(area.getSelectedText().isEmpty()); - - type(A); - - assertEquals(area.getText(), area.getSelectedText()); - } - - } - - public class ShiftDown { - - @Before - public void setup() { - press(SHIFT); - } - - @Test - public void left() { - moveCaretTo(wordStart(1)); - assertTrue(area.getSelectedText().isEmpty()); - - type(LEFT); - - assertEquals(wordEnd(0), area.getCaretPosition()); - assertEquals(" ", area.getSelectedText()); - } - - @Test - public void right() { - moveCaretTo(wordEnd(0)); - assertTrue(area.getSelectedText().isEmpty()); - - type(RIGHT); - - assertEquals(wordStart(1), area.getCaretPosition()); - assertEquals(" ", area.getSelectedText()); - } - - } - - public class ShortcutShiftDown { - - @Before - public void setup() { - press(SHORTCUT, SHIFT); - } - - @Test - public void left() { - moveCaretTo(wordEnd(3)); - assertTrue(area.getSelectedText().isEmpty()); - - // first left goes to boundary of current word - type(LEFT); - - assertEquals(wordStart(3), area.getCaretPosition()); - assertEquals(words[3], area.getSelectedText()); - - // second left skips space and goes to end boundary of prev word - type(LEFT); - - assertEquals(wordStart(2), area.getCaretPosition()); - assertEquals(words[2] + " " + words[3], area.getSelectedText()); - } - - @Test - public void right() { - moveCaretTo(wordStart(0)); - assertTrue(area.getSelectedText().isEmpty()); - - // first right goes to boundary of current word - type(RIGHT); - - assertEquals(wordEnd(0), area.getCaretPosition()); - assertEquals(words[0], area.getSelectedText()); - - // second right skips space and goes to end boundary of next word - type(RIGHT); - - assertEquals(wordEnd(1), area.getCaretPosition()); - assertEquals(words[0] + " " + words[1], area.getSelectedText()); - } - - } - - } - - public class MultiLineGridlikeTextTests extends InlineCssTextAreaAppTest { - - public final String[] lines = { - "01 02 03 04 05", - "11 12 13 14 15", - "21 22 23 24 25", - "31 32 33 34 35", - "41 42 43 44 45" - }; - - private int lineStart(int lineIndex) { - return entityStart(lineIndex, lines); - } - - private int lineEnd(int lineIndex) { - return entityEnd(lineIndex, lines, area); - } - - String fullText = String.join(" ", lines); - - private void moveCaretTo(int position) { - area.moveTo(position); - } - - private void waitForMultiLineRegistration() throws TimeoutException { - // When the stage's width changes, TextFlow does not properly handle API calls to a - // multi-line paragraph immediately. So, wait until it correctly responds - // to the stage width change - Future textFlowIsReady = WaitForAsyncUtils.asyncFx(() -> { - while (area.getParagraphLinesCount(0) != lines.length) { - sleep(10); - } - }); - WaitForAsyncUtils.waitFor(5, TimeUnit.SECONDS, textFlowIsReady); - } - - @Override - public void start(Stage stage) throws Exception { - super.start(stage); - area.setWrapText(true); - area.replaceText(fullText); - - // insures area's text appears exactly as the declaration of `lines` - stage.setWidth(150); - area.setStyle( - "-fx-font-family: monospace;" + - "-fx-font-size: 12pt;" - ); - } - - public class NoModifiers { - - @Before - public void setup() throws TimeoutException { - waitForMultiLineRegistration(); - } - - @Test - public void up() { - moveCaretTo(lineStart(2)); - assertTrue(area.getSelectedText().isEmpty()); - - type(UP); - - assertEquals(lineStart(1), area.getCaretPosition()); - assertTrue(area.getSelectedText().isEmpty()); - } - - @Test - public void down() { - moveCaretTo(lineStart(1)); - assertTrue(area.getSelectedText().isEmpty()); - - type(DOWN); - - assertEquals(lineStart(2), area.getCaretPosition()); - assertTrue(area.getSelectedText().isEmpty()); - } - - @Test - public void home() { - moveCaretTo(lineEnd(1)); - assertTrue(area.getSelectedText().isEmpty()); - - type(HOME); - - assertEquals(lineStart(1), area.getCaretPosition()); - assertTrue(area.getSelectedText().isEmpty()); - } - - @Test - public void end() { - moveCaretTo(lineStart(1)); - assertTrue(area.getSelectedText().isEmpty()); - - type(END); - - assertEquals(lineEnd(1), area.getCaretPosition()); - assertTrue(area.getSelectedText().isEmpty()); - } - - } - - public class ShortcutDown { - - @Before - public void setup() throws TimeoutException { - waitForMultiLineRegistration(); - - press(SHORTCUT); - } - - // up/down do nothing - @Test - public void up() { - assertTrue(area.getSelectedText().isEmpty()); - moveCaretTo(lineStart(2)); - - type(UP); - - assertEquals(lineStart(2), area.getCaretPosition()); - assertTrue(area.getSelectedText().isEmpty()); - } - - @Test - public void down() { - assertTrue(area.getSelectedText().isEmpty()); - moveCaretTo(lineStart(2)); - - type(DOWN); - - assertEquals(lineStart(2), area.getCaretPosition()); - assertTrue(area.getSelectedText().isEmpty()); - } - - @Test - public void home() { - moveCaretTo(lineStart(2)); - assertTrue(area.getSelectedText().isEmpty()); - - type(HOME); - - assertEquals(0, area.getCaretPosition()); - assertTrue(area.getSelectedText().isEmpty()); - } - - @Test - public void end() { - moveCaretTo(lineStart(1)); - assertTrue(area.getSelectedText().isEmpty()); - - type(END); - - assertEquals(area.getLength(), area.getCaretPosition()); - assertTrue(area.getSelectedText().isEmpty()); - } - - } - - public class ShiftDown { - - @Before - public void setup() throws TimeoutException { - waitForMultiLineRegistration(); - - press(SHIFT); - } - - @Test - public void up() { - moveCaretTo(lineStart(2)); - assertTrue(area.getSelectedText().isEmpty()); - - type(UP); - - assertEquals(lineStart(1), area.getCaretPosition()); - assertEquals(lines[1] + " ", area.getSelectedText()); - } - - @Test - public void down() { - moveCaretTo(lineStart(1)); - assertTrue(area.getSelectedText().isEmpty()); - - type(DOWN); - - assertEquals(lineStart(2), area.getCaretPosition()); - assertEquals(lines[1] + " ", area.getSelectedText()); - } - - @Test - public void home() { - moveCaretTo(lineEnd(1)); - assertTrue(area.getSelectedText().isEmpty()); - - type(HOME); - - assertEquals(lineStart(1), area.getCaretPosition()); - assertEquals(lines[1], area.getSelectedText()); - } - - @Test - public void end() { - moveCaretTo(lineStart(1)); - assertTrue(area.getSelectedText().isEmpty()); - - type(END); - - assertEquals(lineEnd(1), area.getCaretPosition()); - assertEquals(lines[1], area.getSelectedText()); - } - - } - - public class ShortcutShiftDown { - - @Before - public void setup() throws TimeoutException { - waitForMultiLineRegistration(); - - press(SHORTCUT, SHIFT); - } - - @Test - public void up() { - moveCaretTo(lineStart(2)); - assertTrue(area.getSelectedText().isEmpty()); - - type(UP); - - assertEquals(lineStart(2), area.getCaretPosition()); - assertTrue(area.getSelectedText().isEmpty()); - } - - @Test - public void down() { - moveCaretTo(lineStart(1)); - assertTrue(area.getSelectedText().isEmpty()); - - type(DOWN); - - assertEquals(lineStart(1), area.getCaretPosition()); - assertTrue(area.getSelectedText().isEmpty()); - } - - @Test - public void home() { - moveCaretTo(area.getLength()); - assertTrue(area.getSelectedText().isEmpty()); - - type(HOME); - - assertEquals(0, area.getCaretPosition()); - assertEquals(area.getText(), area.getSelectedText()); - } - - @Test - public void end() { - moveCaretTo(0); - assertTrue(area.getSelectedText().isEmpty()); - - type(END); - - assertEquals(area.getLength(), area.getCaretPosition()); - assertEquals(area.getText(), area.getSelectedText()); - } - - } - - } - - public class MultiLineJaggedTextTests extends InlineCssTextAreaAppTest { - - String threeLinesOfText = "Some long amount of text to take up a lot of space in the given area."; - - @Override - public void start(Stage stage) throws Exception { - super.start(stage); - stage.setWidth(200); - area.replaceText(threeLinesOfText); - area.setWrapText(true); - } - - private void waitForMultiLineRegistration() throws TimeoutException { - // When the stage's width changes, TextFlow does not properly handle API calls to a - // multi-line paragraph immediately. So, wait until it correctly responds - // to the stage width change - Future textFlowIsReady = WaitForAsyncUtils.asyncFx(() -> { - while (area.getParagraphLinesCount(0) != 3) { - sleep(10); - } - }); - WaitForAsyncUtils.waitFor(5, TimeUnit.SECONDS, textFlowIsReady); - } - - @Test - public void pressingDownMovesCaretToNextLine() throws TimeoutException { - waitForMultiLineRegistration(); - - area.moveTo(27); - - push(DOWN); - - assertEquals(57, area.getCaretPosition()); - } - - @Test - public void pressingUpMovesCaretToPrevLine() throws TimeoutException { - waitForMultiLineRegistration(); - - area.moveTo(66); - - push(UP); - - assertEquals(36, area.getCaretPosition()); - } - } - - public class ViewportTests extends InlineCssTextAreaAppTest { - - @Override - public void start(Stage stage) throws Exception { - super.start(stage); - - // allow 6 lines to be displayed - stage.setHeight(90); - - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < 8; i++) { - sb.append(i).append("\n"); - } - sb.append(9); - area.replaceText(sb.toString()); - } - - @Test - public void testPageUp() { - interact(() -> { - area.moveTo(5, 0); - area.requestFollowCaret(); - }); - assertTrue(area.getSelectedText().isEmpty()); - Bounds beforeBounds = area.getCaretBounds().get(); - - type(PAGE_UP); - - Bounds afterBounds = area.getCaretBounds().get(); - assertEquals(0, area.getCaretPosition()); - assertTrue(area.getSelectedText().isEmpty()); - assertTrue(beforeBounds.getMinY() > afterBounds.getMinY()); - } - - @Ignore("doesn't work despite 'testShiftPageDown' working fine using the same code") - @Test - public void testPageDown() throws Exception { - interact(() -> { - area.moveTo(0); - area.requestFollowCaret(); - }); - assertTrue(area.getSelectedText().isEmpty()); - Bounds beforeBounds = area.getCaretBounds().get(); - - type(PAGE_DOWN); - - Bounds afterBounds = area.getCaretBounds().get(); - assertEquals(area.getAbsolutePosition(5, 0), area.getCaretPosition()); - assertTrue(area.getSelectedText().isEmpty()); - assertTrue(beforeBounds.getMinY() < afterBounds.getMinY()); - } - - @Test - public void testShiftPageUp() { - interact(() -> { - area.moveTo(5, 0); - area.requestFollowCaret(); - }); - assertTrue(area.getSelectedText().isEmpty()); - Bounds beforeBounds = area.getCaretBounds().get(); - - press(SHIFT).type(PAGE_UP).release(SHIFT); - - Bounds afterBounds = area.getCaretBounds().get(); - assertEquals(0, area.getCaretPosition()); - assertEquals(area.getText(0, 0, 5, 0), area.getSelectedText()); - assertTrue(beforeBounds.getMinY() > afterBounds.getMinY()); - } - - @Test - public void testShiftPageDown() { - interact(() -> { - area.moveTo(0); - area.requestFollowCaret(); - }); - assertTrue(area.getSelectedText().isEmpty()); - Bounds beforeBounds = area.getCaretBounds().get(); - - press(SHIFT).type(PAGE_DOWN).release(SHIFT); - - Bounds afterBounds = area.getCaretBounds().get(); - assertEquals(area.getAbsolutePosition(5, 0), area.getCaretPosition()); - assertEquals(area.getText(0, 0, 5, 0), area.getSelectedText()); - assertTrue(beforeBounds.getMinY() < afterBounds.getMinY()); - } - - } -} diff --git a/richtextfx/src/integrationTest/java/org/fxmisc/richtext/keyboard/PageUpDownTests.java b/richtextfx/src/integrationTest/java/org/fxmisc/richtext/keyboard/PageUpDownTests.java new file mode 100644 index 000000000..782913d9e --- /dev/null +++ b/richtextfx/src/integrationTest/java/org/fxmisc/richtext/keyboard/PageUpDownTests.java @@ -0,0 +1,107 @@ +package org.fxmisc.richtext.keyboard; + +import javafx.geometry.Bounds; +import javafx.stage.Stage; +import org.fxmisc.richtext.InlineCssTextAreaAppTest; +import org.junit.Ignore; +import org.junit.Test; + +import static javafx.scene.input.KeyCode.PAGE_DOWN; +import static javafx.scene.input.KeyCode.PAGE_UP; +import static javafx.scene.input.KeyCode.SHIFT; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class PageUpDownTests extends InlineCssTextAreaAppTest { + + private static final String EIGHT_LINES; + + static { + StringBuilder sb = new StringBuilder(); + int totalLines = 8; + for (int i = 0; i < totalLines - 1; i++) { + sb.append(i).append("\n"); + } + sb.append(totalLines); + EIGHT_LINES = sb.toString(); + } + + @Override + public void start(Stage stage) throws Exception { + super.start(stage); + + // allow 6 lines to be displayed + stage.setHeight(90); + area.replaceText(EIGHT_LINES); + } + + @Test + public void page_up_moves_caret_to_top_of_viewport() { + interact(() -> { + area.moveTo(5, 0); + area.requestFollowCaret(); + }); + assertTrue(area.getSelectedText().isEmpty()); + Bounds beforeBounds = area.getCaretBounds().get(); + + type(PAGE_UP); + + Bounds afterBounds = area.getCaretBounds().get(); + assertEquals(0, area.getCaretPosition()); + assertTrue(area.getSelectedText().isEmpty()); + assertTrue(beforeBounds.getMinY() > afterBounds.getMinY()); + } + + @Test + public void page_down_moves_caret_to_bottom_of_viewport() throws Exception { + interact(() -> { + area.moveTo(0); + area.requestFollowCaret(); + }); + assertTrue(area.getSelectedText().isEmpty()); + Bounds beforeBounds = area.getCaretBounds().get(); + + type(PAGE_DOWN); + + Bounds afterBounds = area.getCaretBounds().get(); + assertEquals(area.getAbsolutePosition(5, 0), area.getCaretPosition()); + assertTrue(area.getSelectedText().isEmpty()); + assertTrue(beforeBounds.getMinY() < afterBounds.getMinY()); + } + + @Test + public void shift_page_up_moves_caret_to_top_of_viewport_and_makes_selection() { + interact(() -> { + area.moveTo(5, 0); + area.requestFollowCaret(); + }); + assertTrue(area.getSelectedText().isEmpty()); + Bounds beforeBounds = area.getCaretBounds().get(); + + press(SHIFT).type(PAGE_UP).release(SHIFT); + + Bounds afterBounds = area.getCaretBounds().get(); + assertEquals(0, area.getCaretPosition()); + assertEquals(area.getText(0, 0, 5, 0), area.getSelectedText()); + assertTrue(beforeBounds.getMinY() > afterBounds.getMinY()); + } + + @Test + public void shift_page_down_moves_caret_to_bottom_of_viewport_and_makes_selection() { + interact(() -> { + area.moveTo(0); + area.requestFollowCaret(); + }); + assertTrue(area.getSelectedText().isEmpty()); + Bounds beforeBounds = area.getCaretBounds().get(); + + press(SHIFT).type(PAGE_DOWN).release(SHIFT); + + Bounds afterBounds = area.getCaretBounds().get(); + assertEquals(area.getAbsolutePosition(5, 0), area.getCaretPosition()); + assertEquals(area.getText(0, 0, 5, 0), area.getSelectedText()); + assertTrue(beforeBounds.getMinY() < afterBounds.getMinY()); + } + +} + diff --git a/richtextfx/src/integrationTest/java/org/fxmisc/richtext/keyboard/TypingTests.java b/richtextfx/src/integrationTest/java/org/fxmisc/richtext/keyboard/TypingTests.java index cc8c81b5b..88d4e81e2 100644 --- a/richtextfx/src/integrationTest/java/org/fxmisc/richtext/keyboard/TypingTests.java +++ b/richtextfx/src/integrationTest/java/org/fxmisc/richtext/keyboard/TypingTests.java @@ -9,7 +9,7 @@ public class TypingTests extends InlineCssTextAreaAppTest { @Test - public void typingALetterMovesTheCaretAfterThatInsertedLetter() { + public void typing_a_letter_moves_caret_after_the_inserted_letter() { interact(() -> { area.moveTo(0); area.clear(); diff --git a/richtextfx/src/integrationTest/java/org/fxmisc/richtext/keyboard/navigation/MultiLineGridlikeTextTests.java b/richtextfx/src/integrationTest/java/org/fxmisc/richtext/keyboard/navigation/MultiLineGridlikeTextTests.java new file mode 100644 index 000000000..6afc0ba0e --- /dev/null +++ b/richtextfx/src/integrationTest/java/org/fxmisc/richtext/keyboard/navigation/MultiLineGridlikeTextTests.java @@ -0,0 +1,295 @@ +package org.fxmisc.richtext.keyboard.navigation; + +import com.nitorcreations.junit.runners.NestedRunner; +import javafx.stage.Stage; +import org.fxmisc.richtext.InlineCssTextAreaAppTest; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.testfx.util.WaitForAsyncUtils; + +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import static javafx.scene.input.KeyCode.DOWN; +import static javafx.scene.input.KeyCode.END; +import static javafx.scene.input.KeyCode.HOME; +import static javafx.scene.input.KeyCode.SHIFT; +import static javafx.scene.input.KeyCode.SHORTCUT; +import static javafx.scene.input.KeyCode.UP; +import static org.fxmisc.richtext.keyboard.navigation.Utils.entityEnd; +import static org.fxmisc.richtext.keyboard.navigation.Utils.entityStart; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +@RunWith(NestedRunner.class) +public class MultiLineGridlikeTextTests extends InlineCssTextAreaAppTest { + + public final String[] lines = { + "01 02 03 04 05", + "11 12 13 14 15", + "21 22 23 24 25", + "31 32 33 34 35", + "41 42 43 44 45" + }; + + private int lineStart(int lineIndex) { + return entityStart(lineIndex, lines); + } + + private int lineEnd(int lineIndex) { + return entityEnd(lineIndex, lines, area); + } + + String fullText = String.join(" ", lines); + + private void moveCaretTo(int position) { + area.moveTo(position); + } + + private void waitForMultiLineRegistration() throws TimeoutException { + // When the stage's width changes, TextFlow does not properly handle API calls to a + // multi-line paragraph immediately. So, wait until it correctly responds + // to the stage width change + Future textFlowIsReady = WaitForAsyncUtils.asyncFx(() -> { + while (area.getParagraphLinesCount(0) != lines.length) { + sleep(10); + } + }); + WaitForAsyncUtils.waitFor(5, TimeUnit.SECONDS, textFlowIsReady); + } + + @Override + public void start(Stage stage) throws Exception { + super.start(stage); + area.setWrapText(true); + area.replaceText(fullText); + + // insures area's text appears exactly as the declaration of `lines` + stage.setWidth(150); + area.setStyle( + "-fx-font-family: monospace;" + + "-fx-font-size: 12pt;" + ); + } + + public class When_No_Modifiers_Pressed { + + @Before + public void setup() throws TimeoutException { + waitForMultiLineRegistration(); + } + + @Test + public void up_moves_caret_to_previous_line() { + moveCaretTo(lineStart(2)); + assertTrue(area.getSelectedText().isEmpty()); + + type(UP); + + assertEquals(lineStart(1), area.getCaretPosition()); + assertTrue(area.getSelectedText().isEmpty()); + } + + @Test + public void down_moves_caret_to_next_line() { + moveCaretTo(lineStart(1)); + assertTrue(area.getSelectedText().isEmpty()); + + type(DOWN); + + assertEquals(lineStart(2), area.getCaretPosition()); + assertTrue(area.getSelectedText().isEmpty()); + } + + @Test + public void home_moves_caret_to_start_of_current_line() { + moveCaretTo(lineEnd(1)); + assertTrue(area.getSelectedText().isEmpty()); + + type(HOME); + + assertEquals(lineStart(1), area.getCaretPosition()); + assertTrue(area.getSelectedText().isEmpty()); + } + + @Test + public void end_moves_caret_to_end_of_current_line() { + moveCaretTo(lineStart(1)); + assertTrue(area.getSelectedText().isEmpty()); + + type(END); + + assertEquals(lineEnd(1), area.getCaretPosition()); + assertTrue(area.getSelectedText().isEmpty()); + } + + } + + public class When_Shortcut_Is_Pressed { + + @Before + public void setup() throws TimeoutException { + waitForMultiLineRegistration(); + + press(SHORTCUT); + } + + @Test + public void up_does_not_move_caret() { + assertTrue(area.getSelectedText().isEmpty()); + moveCaretTo(lineStart(2)); + + type(UP); + + assertEquals(lineStart(2), area.getCaretPosition()); + assertTrue(area.getSelectedText().isEmpty()); + } + + @Test + public void down_does_not_move_caret() { + assertTrue(area.getSelectedText().isEmpty()); + moveCaretTo(lineStart(2)); + + type(DOWN); + + assertEquals(lineStart(2), area.getCaretPosition()); + assertTrue(area.getSelectedText().isEmpty()); + } + + @Test + public void home_moves_caret_to_start_of_current_paragraph() { + moveCaretTo(lineStart(2)); + assertTrue(area.getSelectedText().isEmpty()); + + type(HOME); + + assertEquals(0, area.getCaretPosition()); + assertTrue(area.getSelectedText().isEmpty()); + } + + @Test + public void end_moves_caret_to_end_of_current_paragraph() { + moveCaretTo(lineStart(1)); + assertTrue(area.getSelectedText().isEmpty()); + + type(END); + + assertEquals(area.getLength(), area.getCaretPosition()); + assertTrue(area.getSelectedText().isEmpty()); + } + + } + + public class When_Shift_Is_Pressed { + + @Before + public void setup() throws TimeoutException { + waitForMultiLineRegistration(); + + press(SHIFT); + } + + @Test + public void up() { + moveCaretTo(lineStart(2)); + assertTrue(area.getSelectedText().isEmpty()); + + type(UP); + + assertEquals(lineStart(1), area.getCaretPosition()); + assertEquals(lines[1] + " ", area.getSelectedText()); + } + + @Test + public void down() { + moveCaretTo(lineStart(1)); + assertTrue(area.getSelectedText().isEmpty()); + + type(DOWN); + + assertEquals(lineStart(2), area.getCaretPosition()); + assertEquals(lines[1] + " ", area.getSelectedText()); + } + + @Test + public void home_selects_up_to_the_start_of_current_line() { + moveCaretTo(lineEnd(1)); + assertTrue(area.getSelectedText().isEmpty()); + + type(HOME); + + assertEquals(lineStart(1), area.getCaretPosition()); + assertEquals(lines[1], area.getSelectedText()); + } + + @Test + public void end_selects_up_to_the_end_of_current_line() { + moveCaretTo(lineStart(1)); + assertTrue(area.getSelectedText().isEmpty()); + + type(END); + + assertEquals(lineEnd(1), area.getCaretPosition()); + assertEquals(lines[1], area.getSelectedText()); + } + + } + + public class When_Shortcut_And_Shift_Pressed { + + @Before + public void setup() throws TimeoutException { + waitForMultiLineRegistration(); + + press(SHORTCUT, SHIFT); + } + + @Test + public void up_does_not_move_caret() { + moveCaretTo(lineStart(2)); + assertTrue(area.getSelectedText().isEmpty()); + + type(UP); + + assertEquals(lineStart(2), area.getCaretPosition()); + assertTrue(area.getSelectedText().isEmpty()); + } + + @Test + public void down_does_not_move_caret() { + moveCaretTo(lineStart(1)); + assertTrue(area.getSelectedText().isEmpty()); + + type(DOWN); + + assertEquals(lineStart(1), area.getCaretPosition()); + assertTrue(area.getSelectedText().isEmpty()); + } + + @Test + public void home_selects_up_to_the_start_of_current_paragraph() { + moveCaretTo(area.getLength()); + assertTrue(area.getSelectedText().isEmpty()); + + type(HOME); + + assertEquals(0, area.getCaretPosition()); + assertEquals(area.getText(), area.getSelectedText()); + } + + @Test + public void end_selects_up_to_the_end_of_current_paragraph() { + moveCaretTo(0); + assertTrue(area.getSelectedText().isEmpty()); + + type(END); + + assertEquals(area.getLength(), area.getCaretPosition()); + assertEquals(area.getText(), area.getSelectedText()); + } + + } + +} diff --git a/richtextfx/src/integrationTest/java/org/fxmisc/richtext/keyboard/navigation/MultiLineJaggedTextTests.java b/richtextfx/src/integrationTest/java/org/fxmisc/richtext/keyboard/navigation/MultiLineJaggedTextTests.java new file mode 100644 index 000000000..9a76fc5af --- /dev/null +++ b/richtextfx/src/integrationTest/java/org/fxmisc/richtext/keyboard/navigation/MultiLineJaggedTextTests.java @@ -0,0 +1,61 @@ +package org.fxmisc.richtext.keyboard.navigation; + +import javafx.stage.Stage; +import org.fxmisc.richtext.InlineCssTextAreaAppTest; +import org.junit.Test; +import org.testfx.util.WaitForAsyncUtils; + +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import static javafx.scene.input.KeyCode.DOWN; +import static javafx.scene.input.KeyCode.UP; +import static org.junit.Assert.assertEquals; + +public class MultiLineJaggedTextTests extends InlineCssTextAreaAppTest { + + String threeLinesOfText = "Some long amount of text to take up a lot of space in the given area."; + + @Override + public void start(Stage stage) throws Exception { + super.start(stage); + stage.setWidth(200); + area.replaceText(threeLinesOfText); + area.setWrapText(true); + } + + private void waitForMultiLineRegistration() throws TimeoutException { + // When the stage's width changes, TextFlow does not properly handle API calls to a + // multi-line paragraph immediately. So, wait until it correctly responds + // to the stage width change + Future textFlowIsReady = WaitForAsyncUtils.asyncFx(() -> { + while (area.getParagraphLinesCount(0) != 3) { + sleep(10); + } + }); + WaitForAsyncUtils.waitFor(5, TimeUnit.SECONDS, textFlowIsReady); + } + + @Test + public void pressing_down_moves_caret_to_next_line() throws TimeoutException { + waitForMultiLineRegistration(); + + area.moveTo(27); + + push(DOWN); + + assertEquals(57, area.getCaretPosition()); + } + + @Test + public void pressing_up_moves_caret_to_previous_line() throws TimeoutException { + waitForMultiLineRegistration(); + + area.moveTo(66); + + push(UP); + + assertEquals(36, area.getCaretPosition()); + } +} diff --git a/richtextfx/src/integrationTest/java/org/fxmisc/richtext/keyboard/navigation/SingleLineTests.java b/richtextfx/src/integrationTest/java/org/fxmisc/richtext/keyboard/navigation/SingleLineTests.java new file mode 100644 index 000000000..365d28153 --- /dev/null +++ b/richtextfx/src/integrationTest/java/org/fxmisc/richtext/keyboard/navigation/SingleLineTests.java @@ -0,0 +1,220 @@ +package org.fxmisc.richtext.keyboard.navigation; + +import com.nitorcreations.junit.runners.NestedRunner; +import javafx.stage.Stage; +import org.fxmisc.richtext.InlineCssTextAreaAppTest; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static javafx.scene.input.KeyCode.A; +import static javafx.scene.input.KeyCode.LEFT; +import static javafx.scene.input.KeyCode.RIGHT; +import static javafx.scene.input.KeyCode.SHIFT; +import static javafx.scene.input.KeyCode.SHORTCUT; +import static org.fxmisc.richtext.keyboard.navigation.Utils.entityEnd; +import static org.fxmisc.richtext.keyboard.navigation.Utils.entityStart; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +@RunWith(NestedRunner.class) +public class SingleLineTests extends InlineCssTextAreaAppTest { + + String[] words = { "the", "cat", "can", "walk" }; + String fullText = String.join(" ", words); + + private int wordStart(int wordIndex) { + return entityStart(wordIndex, words); + } + + private int wordEnd(int wordIndex) { + return entityEnd(wordIndex, words, area); + } + + private void moveCaretTo(int position) { + area.moveTo(position); + } + + @Override + public void start(Stage stage) throws Exception { + super.start(stage); + stage.setWidth(300); + area.replaceText(fullText); + } + + public class When_No_Modifiers_Pressed { + + @Test + public void left_moves_caret_one_position() { + moveCaretTo(wordStart(1)); + assertTrue(area.getSelectedText().isEmpty()); + + type(LEFT); + + assertEquals(wordEnd(0), area.getCaretPosition()); + assertTrue(area.getSelectedText().isEmpty()); + } + + @Test + public void right_moves_caret_one_position() { + moveCaretTo(wordStart(1)); + assertTrue(area.getSelectedText().isEmpty()); + + type(RIGHT); + + assertEquals(wordStart(1) + 1, area.getCaretPosition()); + assertTrue(area.getSelectedText().isEmpty()); + } + + } + + public class When_Shortcut_Is_Pressed { + + @Before + public void setup() { + press(SHORTCUT); + } + + @Test + public void left_once_moves_caret_to_left_boundary_of_current_word() { + moveCaretTo(wordEnd(3)); + assertTrue(area.getSelectedText().isEmpty()); + + // first left goes to boundary of current word + type(LEFT); + + assertEquals(wordStart(3), area.getCaretPosition()); + assertTrue(area.getSelectedText().isEmpty()); + } + + @Test + public void left_twice_moves_caret_to_left_boundary_of_previous_word() { + moveCaretTo(wordEnd(3)); + assertTrue(area.getSelectedText().isEmpty()); + + type(LEFT).type(LEFT); + + assertEquals(wordStart(2), area.getCaretPosition()); + assertTrue(area.getSelectedText().isEmpty()); + } + + @Test + public void right_once_moves_caret_to_right_boundary_of_current_word() { + moveCaretTo(wordStart(0)); + assertTrue(area.getSelectedText().isEmpty()); + + // first right goes to boundary of current word + type(RIGHT); + + assertEquals(wordEnd(0), area.getCaretPosition()); + assertTrue(area.getSelectedText().isEmpty()); + } + + @Test + public void right_twice_moves_caret_to_right_boundary_of_next_word() { + moveCaretTo(wordStart(0)); + assertTrue(area.getSelectedText().isEmpty()); + + type(RIGHT).type(RIGHT); + + assertEquals(wordEnd(1), area.getCaretPosition()); + assertTrue(area.getSelectedText().isEmpty()); + } + + @Test + public void a_selects_all() { + assertTrue(area.getSelectedText().isEmpty()); + + type(A); + + assertEquals(area.getText(), area.getSelectedText()); + } + + } + + public class When_Shift_Is_Pressed { + + @Before + public void setup() { + press(SHIFT); + } + + @Test + public void left_selects_previous_character() { + moveCaretTo(wordStart(1)); + assertTrue(area.getSelectedText().isEmpty()); + + type(LEFT); + + assertEquals(wordEnd(0), area.getCaretPosition()); + assertEquals(" ", area.getSelectedText()); + } + + @Test + public void right_selects_next_character() { + moveCaretTo(wordEnd(0)); + assertTrue(area.getSelectedText().isEmpty()); + + type(RIGHT); + + assertEquals(wordStart(1), area.getCaretPosition()); + assertEquals(" ", area.getSelectedText()); + } + + } + + public class When_Shortcut_And_Shift_Pressed { + + @Before + public void setup() { + press(SHORTCUT, SHIFT); + } + + @Test + public void left_once_selects_up_to_left_boundary_of_current_word() { + moveCaretTo(wordEnd(3)); + assertTrue(area.getSelectedText().isEmpty()); + + // first left goes to boundary of current word + type(LEFT); + + assertEquals(wordStart(3), area.getCaretPosition()); + assertEquals(words[3], area.getSelectedText()); + } + + @Test + public void left_twice_selects_up_to_start_boundary_of_previous_word() { + moveCaretTo(wordEnd(3)); + assertTrue(area.getSelectedText().isEmpty()); + + type(LEFT).type(LEFT); + + assertEquals(wordStart(2), area.getCaretPosition()); + assertEquals(words[2] + " " + words[3], area.getSelectedText()); + } + + @Test + public void right_once_selects_up_to_right_boundary_of_current_word() { + moveCaretTo(wordStart(0)); + assertTrue(area.getSelectedText().isEmpty()); + + type(RIGHT); + + assertEquals(wordEnd(0), area.getCaretPosition()); + assertEquals(words[0], area.getSelectedText()); + } + + @Test + public void right_twice_selects_up_to_end_boundary_of_next_word() { + moveCaretTo(wordStart(0)); + assertTrue(area.getSelectedText().isEmpty()); + + type(RIGHT).type(RIGHT); + + assertEquals(wordEnd(1), area.getCaretPosition()); + assertEquals(words[0] + " " + words[1], area.getSelectedText()); + } + + } + +} \ No newline at end of file diff --git a/richtextfx/src/integrationTest/java/org/fxmisc/richtext/keyboard/navigation/Utils.java b/richtextfx/src/integrationTest/java/org/fxmisc/richtext/keyboard/navigation/Utils.java new file mode 100644 index 000000000..627084df9 --- /dev/null +++ b/richtextfx/src/integrationTest/java/org/fxmisc/richtext/keyboard/navigation/Utils.java @@ -0,0 +1,32 @@ +package org.fxmisc.richtext.keyboard.navigation; + +import org.fxmisc.richtext.GenericStyledArea; + +import java.util.Arrays; + +public final class Utils { + + private Utils() { + throw new IllegalStateException("Cannot instantiate Utils class"); + } + + public static int entityStart(int entityIndex, String[] array) { + if (entityIndex == 0) { + return 0; + } else { + return Arrays.stream(array) + .map(String::length) + .limit(entityIndex) + .reduce(0, (a, b) -> a + b) + + entityIndex; // for delimiter characters + } + } + + public static int entityEnd(int entityIndex, String[] array, GenericStyledArea area) { + if (entityIndex == array.length - 1) { + return area.getLength(); + } else { + return entityStart(entityIndex + 1, array) - 1; + } + } +} diff --git a/richtextfx/src/integrationTest/java/org/fxmisc/richtext/mouse/ClickAndDragTests.java b/richtextfx/src/integrationTest/java/org/fxmisc/richtext/mouse/ClickAndDragTests.java index bc2a576bd..727b69b83 100644 --- a/richtextfx/src/integrationTest/java/org/fxmisc/richtext/mouse/ClickAndDragTests.java +++ b/richtextfx/src/integrationTest/java/org/fxmisc/richtext/mouse/ClickAndDragTests.java @@ -1,6 +1,7 @@ package org.fxmisc.richtext.mouse; import com.nitorcreations.junit.runners.NestedRunner; +import javafx.geometry.Bounds; import javafx.stage.Stage; import org.fxmisc.richtext.InlineCssTextAreaAppTest; import org.junit.Test; @@ -15,7 +16,7 @@ @RunWith(NestedRunner.class) public class ClickAndDragTests { - public class WhenAreaIsDisabled extends InlineCssTextAreaAppTest { + public class When_Area_Is_Disabled extends InlineCssTextAreaAppTest { @Override public void start(Stage stage) throws Exception { @@ -26,7 +27,7 @@ public void start(Stage stage) throws Exception { } @Test - public void shiftClickingAreaDoesNothing() { + public void shift_clicking_area_does_nothing() { moveTo(firstLineOfArea()) .moveBy(20, 0) .press(SHIFT) @@ -36,28 +37,28 @@ public void shiftClickingAreaDoesNothing() { } @Test - public void singleClickingAreaDoesNothing() { + public void single_clicking_area_does_nothing() { leftClickOnFirstLine(); assertFalse(area.isFocused()); } @Test - public void doubleClickingAreaDoesNothing() { + public void double_clicking_area_does_nothing() { doubleClickOnFirstLine(); assertFalse(area.isFocused()); } @Test - public void tripleClickingAreaDoesNothing() { + public void triple_clicking_area_does_nothing() { tripleClickOnFirstLine(); assertFalse(area.isFocused()); } @Test - public void draggingTheMouseDoesNotSelectText() { + public void dragging_the_mouse_does_not_select_text() { moveTo(firstLineOfArea()) .press(PRIMARY) .moveBy(20, 0); @@ -66,7 +67,7 @@ public void draggingTheMouseDoesNotSelectText() { } @Test - public void releasingTheMouseAfterDragDoesNothing() { + public void releasing_the_mouse_after_drag_does_nothing() { assertEquals(0, area.getCaretPosition()); moveTo(firstLineOfArea()) @@ -78,9 +79,9 @@ public void releasingTheMouseAfterDragDoesNothing() { } - public class WhenAreaIsEnabled { + public class When_Area_Is_Enabled { - public class AndTextIsNotSelected extends InlineCssTextAreaAppTest { + public class And_Text_Is_Not_Selected extends InlineCssTextAreaAppTest { private String firstWord = "Some"; private String firstParagraph = firstWord + " text goes here"; @@ -93,32 +94,33 @@ public void start(Stage stage) throws Exception { } @Test - public void singleClickingAreaMovesCaretToThatPosition() { + public void single_clicking_area_moves_caret_to_that_position() { assertEquals(0, area.getCaretPosition()); - moveTo(firstLineOfArea()) - .moveBy(20, 0) - .clickOn(PRIMARY); + Bounds bounds = area.getCharacterBoundsOnScreen( + firstWord.length(), firstWord.length() + 1).get(); + + moveTo(bounds).clickOn(PRIMARY); - assertTrue(0 != area.getCaretPosition()); + assertEquals(firstWord.length(), area.getCaretPosition()); } @Test - public void doubleClickingTextInAreaSelectsClosestWord() { + public void double_clicking_text_in_area_selects_closest_word() { doubleClickOnFirstLine(); assertEquals(firstWord, area.getSelectedText()); } @Test - public void tripleClickingLineInAreaSelectsParagraph() { + public void triple_clicking_line_in_area_selects_paragraph() { tripleClickOnFirstLine(); assertEquals(firstParagraph, area.getSelectedText()); } @Test - public void pressingMouseOverTextAndDraggingMouseSelectsText() { + public void pressing_mouse_over_text_and_dragging_mouse_selects_text() { moveTo(firstLineOfArea()) .press(PRIMARY) .moveBy(20, 0); @@ -128,29 +130,30 @@ public void pressingMouseOverTextAndDraggingMouseSelectsText() { } - public class AndTextIsSelected extends InlineCssTextAreaAppTest { + public class And_Text_Is_Selected extends InlineCssTextAreaAppTest { private String firstWord = "Some"; private String firstParagraph = firstWord + " text goes here"; private String extraText = "This is extra text"; @Test - public void singleClickingWithinSelectedTextMovesCaretToThatPosition() { + public void single_clicking_within_selected_text_moves_caret_to_that_position() { // setup interact(() -> { area.replaceText(firstParagraph); area.selectAll(); }); - moveTo(firstLineOfArea()) - .moveBy(20, 0) - .clickOn(PRIMARY); + Bounds bounds = area.getCharacterBoundsOnScreen( + firstWord.length(), firstWord.length() + 1).get(); + + moveTo(bounds).clickOn(PRIMARY); - assertTrue(0 != area.getCaretPosition()); + assertEquals(firstWord.length(), area.getCaretPosition()); } @Test - public void doubleClickingWithinSelectedTextSelectsClosestWord() { + public void double_clicking_within_selected_text_selects_closest_word() { // setup interact(() -> { area.replaceText(firstParagraph); @@ -163,7 +166,7 @@ public void doubleClickingWithinSelectedTextSelectsClosestWord() { } @Test - public void tripleClickingWithinSelectedTextSelectsParagraph() { + public void triple_clicking_within_selected_text_selects_paragraph() { // setup interact(() -> { area.replaceText(firstParagraph); @@ -176,24 +179,23 @@ public void tripleClickingWithinSelectedTextSelectsParagraph() { } @Test - public void singleClickingOutsideOfSelectedTextMovesCaretToThatPosition() { + public void single_clicking_outside_of_selected_text_moves_caret_to_that_position() { // setup interact(() -> { area.replaceText(firstParagraph + "\n" + "this is the selected text"); area.selectRange(1, 0, 2, -1); }); - int caretPos = area.getCaretPosition(); + Bounds bounds = area.getCharacterBoundsOnScreen( + firstWord.length(), firstWord.length() + 1).get(); - moveTo(firstLineOfArea()) - .moveBy(20, 0) - .clickOn(PRIMARY); + moveTo(bounds).clickOn(PRIMARY); - assertTrue(caretPos != area.getCaretPosition()); + assertEquals(firstWord.length(), area.getCaretPosition()); } @Test - public void doubleClickingOutsideOfSelectedTextSelectsClosestWord() { + public void double_clicking_outside_of_selected_text_selects_closest_word() { // setup interact(() -> { area.replaceText(firstParagraph + "\n" + "this is the selected text"); @@ -206,7 +208,7 @@ public void doubleClickingOutsideOfSelectedTextSelectsClosestWord() { } @Test - public void tripleClickingOutsideOfSelectedTextSelectsParagraph() { + public void triple_clicking_outside_of_selected_text_selects_paragraph() { // setup interact(() -> { area.replaceText(firstParagraph + "\n" + "this is the selected text"); @@ -219,7 +221,7 @@ public void tripleClickingOutsideOfSelectedTextSelectsParagraph() { } @Test - public void pressingMouseOnUnselectedTextAndDraggingMakesNewSelection() { + public void pressing_mouse_on_unselected_text_and_dragging_makes_new_selection() { // setup interact(() -> { area.replaceText(firstParagraph + "\n" + "this is the selected text"); @@ -236,41 +238,56 @@ public void pressingMouseOnUnselectedTextAndDraggingMakesNewSelection() { } @Test - public void pressingMouseOnSelectionAndDraggingDisplacesCaret() { + public void pressing_mouse_on_selection_and_dragging_displaces_caret() { // setup interact(() -> { area.replaceText(firstParagraph + "\n" + extraText); - area.selectRange(0, firstWord.length()); + area.selectRange(0, firstParagraph.length()); }); String selText = area.getSelectedText(); - moveTo(firstLineOfArea()) + Bounds firstLetterBounds = area.getCharacterBoundsOnScreen(1, 2).get(); + Bounds firstWordEndBounds = area.getCharacterBoundsOnScreen( + firstWord.length(), firstWord.length() + 1).get(); + + moveTo(firstLetterBounds) .press(PRIMARY) - .moveBy(0, 22); + .moveTo(firstWordEndBounds); - assertEquals(firstParagraph.length() + 1, area.getCaretPosition()); + assertEquals(firstWord.length(), area.getCaretPosition()); assertEquals(selText, area.getSelectedText()); } @Test - public void pressingMouseOnSelectionAndDraggingAndReleasingMovesSelectedTextToThatPosition() { + public void pressing_mouse_on_selection_and_dragging_and_releasing_moves_selected_text_to_that_position() { + // Linux passes; Mac fails at "assertEquals(selText, area.getSelectedText())"; Windows is untested + // so only run on Linux + // TODO: update test to see if it works on Windows + run_only_on_linux(); + // setup + String twoSpaces = " "; interact(() -> { - area.replaceText(firstParagraph + "\n" + extraText); + area.replaceText(firstParagraph + "\n" + twoSpaces + extraText); area.selectRange(0, firstWord.length()); }); String selText = area.getSelectedText(); - moveTo(firstLineOfArea()) + Bounds letterInFirstWord = area.getCharacterBoundsOnScreen(1, 2).get(); + + int insertionPosition = firstParagraph.length() + 2; + Bounds insertionBounds = area.getCharacterBoundsOnScreen(insertionPosition, insertionPosition + 1).get(); + + moveTo(letterInFirstWord) .press(PRIMARY) - .dropBy(0, 22); + .dropTo(insertionBounds); String expectedText = firstParagraph.substring(firstWord.length()) - + "\n" + firstWord + extraText; + + "\n" + " " + firstWord + " " + extraText; - assertEquals(firstParagraph.length() + 1, area.getCaretPosition()); + assertEquals(insertionPosition, area.getCaretPosition()); assertEquals(selText, area.getSelectedText()); assertEquals(expectedText, area.getText()); } diff --git a/richtextfx/src/integrationTest/java/org/fxmisc/richtext/mouse/ContextMenuTests.java b/richtextfx/src/integrationTest/java/org/fxmisc/richtext/mouse/ContextMenuTests.java index 377a87b6e..010dde521 100644 --- a/richtextfx/src/integrationTest/java/org/fxmisc/richtext/mouse/ContextMenuTests.java +++ b/richtextfx/src/integrationTest/java/org/fxmisc/richtext/mouse/ContextMenuTests.java @@ -2,9 +2,13 @@ import javafx.geometry.Point2D; import javafx.geometry.Pos; +import javafx.scene.control.ContextMenu; +import javafx.scene.control.MenuItem; import javafx.scene.input.KeyCode; import javafx.scene.input.MouseButton; +import javafx.stage.Stage; import org.fxmisc.richtext.InlineCssTextAreaAppTest; +import org.junit.After; import org.junit.Test; import static junit.framework.TestCase.assertFalse; @@ -12,8 +16,27 @@ public class ContextMenuTests extends InlineCssTextAreaAppTest { + private ContextMenu menu; + + // offset needs to be 5+ to prevent test failures + private double offset = 30; + + @Override + public void start(Stage stage) throws Exception { + super.start(stage); + menu = new ContextMenu(new MenuItem("A menu item")); + area.setContextMenu(menu); + area.setContextMenuXOffset(offset); + area.setContextMenuYOffset(offset); + } + + @After + public void cleanup() { + interact(menu::hide); + } + @Test - public void clickingSecondaryShowsContextMenu() { + public void clicking_secondary_shows_context_menu() { // Linux passes; Mac fails; Windows untested // so for now, only run on Linux // TODO: See if tests pass on Windows @@ -27,7 +50,7 @@ public void clickingSecondaryShowsContextMenu() { } @Test - public void pressingSecondaryShowsContextMenu() { + public void pressing_secondary_shows_context_menu() { // Linux passes; Mac fails; Windows untested // so for now, only run on Linux // TODO: See if tests pass on Windows @@ -41,25 +64,27 @@ public void pressingSecondaryShowsContextMenu() { } @Test - public void pressingPrimaryMouseButtonHidesContextMenu() { + public void pressing_primary_mouse_button_hides_context_menu() { // given menu is showing - showContextMenuAt(Pos.TOP_LEFT, 30); + showContextMenuAt(); moveTo(firstLineOfArea()).press(MouseButton.PRIMARY); + assertFalse(area.getContextMenu().isShowing()); } @Test - public void pressingMiddleMouseButtonHidesContextMenu() { + public void pressing_middle_mouse_button_hides_context_menu() { // given menu is showing - showContextMenuAt(Pos.TOP_LEFT, 30); + showContextMenuAt(); moveTo(firstLineOfArea()).press(MouseButton.MIDDLE); + assertFalse(area.getContextMenu().isShowing()); } @Test - public void requestingContextMenuViaKeyboardWorksOnWindows() { + public void requesting_context_nenu_via_keyboard_works_on_windows() { run_only_on_windows(); leftClickOnFirstLine(); @@ -68,8 +93,8 @@ public void requestingContextMenuViaKeyboardWorksOnWindows() { assertTrue(area.getContextMenu().isShowing()); } - private void showContextMenuAt(Pos posInArea, double offset) { - Point2D screenPoint = point(area).atPosition(posInArea).atOffset(offset, offset).query(); + private void showContextMenuAt() { + Point2D screenPoint = position(Pos.TOP_LEFT, offset, offset).query(); interact(() -> area.getContextMenu().show(area, screenPoint.getX(), screenPoint.getY())); }